diff --git a/mods/.caverealms_old/LICENSE.txt b/mods/.caverealms_old/LICENSE.txt new file mode 100644 index 00000000..6e9767ff --- /dev/null +++ b/mods/.caverealms_old/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2017, Craig Robbins and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. diff --git a/mods/.caverealms_old/README.md b/mods/.caverealms_old/README.md new file mode 100644 index 00000000..4ab76b83 --- /dev/null +++ b/mods/.caverealms_old/README.md @@ -0,0 +1,31 @@ +# caverealms-lite + +Based on the original minetest-caverealms mod (https://github.com/HeroOfTheWinds/minetest-caverealms/). + +Adds underground realms to minetest. + +This caverealms fork provides all the biomes and decorations from the original caverealms, with several additions and without the overhead of generating caves. This lowers the server resources the mod requires, for example CPU and RAM. This also removes the large lava spills created by the original caverealms. + +It is specifically written to work with the mgvalleys mapgen, but will work using other mapgens as well. The mapgen used will determine the shape and size of individual caves. Mapgens that generate only smaller caves may be less suitable for use with this fork than mgvalleys. + +Note: For worlds where the original caverealms is already in use, this fork is not advised as a replacement. If used in this way, some unknown nodes and other minor issues should be expected. + + +## License and Contributors + +Source code: FreeBSD License (Simplified) +The original caverealms was licensed as WTFPL. + +Contributors: +- Zeno, Shara RedCat - This rewrite +- HeroOfTheWinds, Zeno - Original mod + + +## Recommended Additions + +- VanessaE's HDX texturepacks provide alternative textures. For example, +https://gitlab.com/VanessaE/hdx-128. +- ethereal mod unlocks additional content (https://notabug.org/tenplus1/ethereal). +- mobs_monster mod allows Dungeon Masters to spawn in the Dungeon Master's Lair biome (https://notabug.org/tenplus1/mobs_monster). +- mobs_redo is required to run mobs_monster (https://notabug.org/tenplus1/mobs_redo). +- abritorch adds coloured torches made with caverealms items (https://github.com/Ezhh/abritorch). diff --git a/mods/caverealms/biomes.lua b/mods/.caverealms_old/biomes.lua similarity index 100% rename from mods/caverealms/biomes.lua rename to mods/.caverealms_old/biomes.lua diff --git a/mods/.caverealms_old/config.lua b/mods/.caverealms_old/config.lua new file mode 100644 index 00000000..265ccb00 --- /dev/null +++ b/mods/.caverealms_old/config.lua @@ -0,0 +1,53 @@ +local CONFIG_FILE_PREFIX = "caverealms." + +caverealms.config = {} + +-- This function based on kaeza/minetest-irc/config.lua and used under the +-- terms of BSD 2-clause license. +local function setting(stype, name, default) + local value + if stype == "bool" then + value = minetest.settings:get_bool(CONFIG_FILE_PREFIX..name) + elseif stype == "string" then + value = minetest.settings:get(CONFIG_FILE_PREFIX..name) + elseif stype == "number" then + value = tonumber(minetest.settings:get(CONFIG_FILE_PREFIX..name)) + end + if value == nil then + value = default + end + caverealms.config[name] = value +end + +--generation settings +setting("number", "ymin", -10000) --bottom realm limit +setting("number", "ymax", -4096) --top realm limit +setting("number", "tcave", 0.75) --cave threshold + +--decoration chances +setting("number", "stagcha", 0.03) --chance of stalagmites (was 0.003) +setting("number", "stalcha", 0.03) --chance of stalactites(was 0.003) + +setting("number", "h_lag", 8) --max height for stalagmites +setting("number", "h_lac", 8) --...stalactites +setting("number", "crystal", 0.02) --chance of glow crystal formations (was 0.0002) +setting("number", "h_cry", 8) --max height of glow crystals +setting("number", "h_clac", 8) --max height of glow crystal stalactites + +setting("number", "gemcha", 0.03) --chance of small glow gems +setting("number", "mushcha", 0.04) --chance of mushrooms +setting("number", "myccha", 0.03) --chance of mycena mushrooms +setting("number", "wormcha", 0.015) --chance of glow worms +setting("number", "giantcha", 0.001) --chance of giant mushrooms +setting("number", "icicha", 0.035) --chance of icicles +setting("number", "flacha", 0.04) --chance of constant flames + +--realm limits for Dungeon Masters' Lair +setting("number", "dm_top", -8000) --upper limit +setting("number", "dm_bot", -10000) --lower limit + +--should DMs spawn in DM Lair? +setting("bool", "dm_spawn", true) + +--Deep cave settings +setting("number", "deep_cave", -7000) -- upper limit diff --git a/mods/.caverealms_old/crafting.lua b/mods/.caverealms_old/crafting.lua new file mode 100644 index 00000000..52da2771 --- /dev/null +++ b/mods/.caverealms_old/crafting.lua @@ -0,0 +1,145 @@ +--thin ice to water +minetest.register_craft({ + output = "default:water_source", + type = "shapeless", + recipe = {"caverealms:thin_ice"} +}) + +--use for coal dust +minetest.register_craft({ + output = "default:coalblock", + recipe = { + {"caverealms:coal_dust","caverealms:coal_dust","caverealms:coal_dust"}, + {"caverealms:coal_dust","caverealms:coal_dust","caverealms:coal_dust"}, + {"caverealms:coal_dust","caverealms:coal_dust","caverealms:coal_dust"} + } +}) + +-- DM statue +minetest.register_craft({ + output = "caverealms:dm_statue", + recipe = { + {"caverealms:glow_ore","caverealms:hot_cobble","caverealms:glow_ore"}, + {"caverealms:hot_cobble","caverealms:hot_cobble","caverealms:hot_cobble"}, + {"caverealms:hot_cobble","caverealms:hot_cobble","caverealms:hot_cobble"} + } +}) + +-- Glow obsidian brick +minetest.register_craft({ + output = "caverealms:glow_obsidian_brick 4", + recipe = { + {"caverealms:glow_obsidian", "caverealms:glow_obsidian"}, + {"caverealms:glow_obsidian", "caverealms:glow_obsidian"} + } +}) + +minetest.register_craft({ + output = "caverealms:glow_obsidian_brick_2 4", + recipe = { + {"caverealms:glow_obsidian_2", "caverealms:glow_obsidian_2"}, + {"caverealms:glow_obsidian_2", "caverealms:glow_obsidian_2"} + } +}) + +-- Glow obsidian glass +minetest.register_craft({ + output = "caverealms:glow_obsidian_glass 5", + recipe = { + {"default:glass", "default:glass", "default:glass"}, + {"default:glass", "default:glass", "caverealms:glow_obsidian"} + } +}) + +minetest.register_craft({ + output = "caverealms:glow_obsidian_glass 5", + recipe = { + {"default:glass", "default:glass", "default:glass"}, + {"default:glass", "default:glass", "caverealms:glow_obsidian_2"} + } +}) + +-- Requires ethereal:fish_raw +if minetest.get_modpath("ethereal") then + + -- Professional Fishing Rod + minetest.register_craftitem("caverealms:angler_rod", { + description = "Pro Fishing Rod", + inventory_image = "caverealms_angler_rod.png", + wield_image = "caverealms_angler_rod.png" + }) + + minetest.register_craft({ + output = "caverealms:angler_rod", + recipe = { + {"","","default:steel_ingot"}, + {"", "default:steel_ingot", "caverealms:mushroom_gills"}, + {"default:steel_ingot", "", "caverealms:mushroom_gills"}, + } + }) + + -- Glow Bait + minetest.register_craftitem("caverealms:glow_bait", { + description = "Glow Bait", + inventory_image = "caverealms_glow_bait.png", + wield_image = "caverealms_glow_bait.png", + }) + + minetest.register_craft({ + output = "caverealms:glow_bait 9", + recipe = { + {"caverealms:glow_worm_green"}, + } + }) + + -- default ethereal fish + local fish = { + {"ethereal:fish_raw"}, + } + + -- Pro Fishing Rod (Baited) + minetest.register_craftitem("caverealms:angler_rod_baited", { + description = "Baited Pro Fishing Rod", + inventory_image = "caverealms_angler_rod_baited.png", + wield_image = "caverealms_angler_rod_weild.png", + stack_max = 1, + liquids_pointable = true, + + on_use = function (itemstack, user, pointed_thing) + + if pointed_thing.type ~= "node" then + return + end + + local node = minetest.get_node(pointed_thing.under).name + + if (node == "default:water_source" + or node == "default:river_water_source") + and math.random(1, 100) < 35 then + + local type = fish[math.random(1, #fish)][1] + local inv = user:get_inventory() + + if inv:room_for_item("main", {name = type}) then + + inv:add_item("main", {name = type}) + + if (math.random() < 0.6) then + return ItemStack("caverealms:angler_rod_baited") + else + return ItemStack("caverealms:angler_rod") + end + else + minetest.chat_send_player(user:get_player_name(), + "Inventory full, Fish Got Away!") + end + end + end, + }) + + minetest.register_craft({ + type = "shapeless", + output = "caverealms:angler_rod_baited", + recipe = {"caverealms:angler_rod", "caverealms:glow_bait"}, + }) +end diff --git a/mods/caverealms/depends.txt b/mods/.caverealms_old/depends.txt similarity index 100% rename from mods/caverealms/depends.txt rename to mods/.caverealms_old/depends.txt diff --git a/mods/.caverealms_old/description.txt b/mods/.caverealms_old/description.txt new file mode 100644 index 00000000..23587508 --- /dev/null +++ b/mods/.caverealms_old/description.txt @@ -0,0 +1 @@ +A mod for Minetest to add underground realms. diff --git a/mods/.caverealms_old/docs/caverealms_lite_biomes.txt b/mods/.caverealms_old/docs/caverealms_lite_biomes.txt new file mode 100644 index 00000000..d1591eeb --- /dev/null +++ b/mods/.caverealms_old/docs/caverealms_lite_biomes.txt @@ -0,0 +1,19 @@ +Biome #, Biome name, "floor node" + + +0, None +1, Moss, "caverealms:stone_with_moss" +2, Fungal, "caverealms:stone_with_lichen" +3, Algae, "caverealms:stone_with_algae" +4, Glaciated, "caverealms:thin_ice" + +The following are "deep realms" + +5, Deep Glaciated, "default:ice" +6, DM, "caverealms:hot_cobble" +7, Salt Crystal, "caverealms:stone_with_salt" +8, Glow Obsidian, "caverealms:glow_obsidian" + OR "caverealms:glow_obsidian2" +9, Coal, "default:coalblock" + OR "caverealms:coal_dust" + OR "default:desert_sand" diff --git a/mods/.caverealms_old/dungeon_master.lua b/mods/.caverealms_old/dungeon_master.lua new file mode 100644 index 00000000..e93ea8c2 --- /dev/null +++ b/mods/.caverealms_old/dungeon_master.lua @@ -0,0 +1,16 @@ +mobs:spawn({ + name = "mobs_monster:dungeon_master", + nodes = {"caverealms:hot_cobble"}, + max_light = 12, + min_light = 0, + chance = 7000, + active_object_count = 2, + max_height = -8000, + on_spawn = function(self, pos) + self.hp_max = 70 + self.health = 70 + self.damage = 5 + self.shoot_interval = 1.5 + self.dogshoot_switch = 0 + end +}) diff --git a/mods/.caverealms_old/functions.lua b/mods/.caverealms_old/functions.lua new file mode 100644 index 00000000..2be9e106 --- /dev/null +++ b/mods/.caverealms_old/functions.lua @@ -0,0 +1,409 @@ +local H_LAG = caverealms.config.h_lag --15 --max height for stalagmites +local H_LAC = caverealms.config.h_lac --20 --...stalactites +local H_CRY = caverealms.config.h_cry --9 --max height of glow crystals +local H_CLAC = caverealms.config.h_clac --13 --max height of glow crystal stalactites + +function caverealms:above_solid(x,y,z,area,data) + local c_air = minetest.get_content_id("air") + + local ai = area:index(x,y+1,z-3) + if data[ai] == c_air then + return false + else + return true + end +end + +function caverealms:below_solid(x,y,z,area,data) + local c_air = minetest.get_content_id("air") + + local ai = area:index(x,y-1,z-3) + if data[ai] == c_air then + return false + else + return true + end +end + +--stalagmite spawner +function caverealms:stalagmite(x,y,z, area, data) + + if not caverealms:below_solid(x,y,z,area,data) then + return + end + + --contest ids + local c_stone = minetest.get_content_id("default:stone") + + local top = math.random(6,H_LAG) --grab a random height for the stalagmite + for j = 0, top do --y + for k = -3, 3 do + for l = -3, 3 do + if j == 0 then + if k*k + l*l <= 9 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = c_stone + end + elseif j <= top/5 then + if k*k + l*l <= 4 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = c_stone + end + elseif j <= top/5 * 3 then + if k*k + l*l <= 1 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = c_stone + end + else + local vi = area:index(x, y+j, z-3) + data[vi] = c_stone + end + end + end + end +end + +--stalactite spawner +function caverealms:stalactite(x,y,z, area, data) + + if not caverealms:above_solid(x,y,z,area,data) then + return + end + + --contest ids + local c_stone = minetest.get_content_id("default:stone")--("caverealms:limestone") + + local bot = math.random(-H_LAC, -6) --grab a random height for the stalagmite + for j = bot, 0 do --y + for k = -3, 3 do + for l = -3, 3 do + if j >= -1 then + if k*k + l*l <= 9 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = c_stone + end + elseif j >= bot/5 then + if k*k + l*l <= 4 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = c_stone + end + elseif j >= bot/5 * 3 then + if k*k + l*l <= 1 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = c_stone + end + else + local vi = area:index(x, y+j, z-3) + data[vi] = c_stone + end + end + end + end +end + +--glowing crystal stalagmite spawner +function caverealms:crystal_stalagmite(x,y,z, area, data, biome) + + if not caverealms:below_solid(x,y,z,area,data) then + return + end + + --contest ids + local c_stone = minetest.get_content_id("default:stone") + local c_crystal = minetest.get_content_id("caverealms:glow_crystal") + local c_crystore = minetest.get_content_id("caverealms:glow_ore") + local c_emerald = minetest.get_content_id("caverealms:glow_emerald") + local c_emore = minetest.get_content_id("caverealms:glow_emerald_ore") + local c_mesecry = minetest.get_content_id("caverealms:glow_mese") + local c_meseore = minetest.get_content_id("default:stone_with_mese") + local c_ruby = minetest.get_content_id("caverealms:glow_ruby") + local c_rubore = minetest.get_content_id("caverealms:glow_ruby_ore") + local c_ameth = minetest.get_content_id("caverealms:glow_amethyst") + local c_amethore = minetest.get_content_id("caverealms:glow_amethyst_ore") + local c_ice = minetest.get_content_id("default:ice") + local c_thinice = minetest.get_content_id("caverealms:thin_ice") + + --for randomness + local mode = 1 + if math.random(15) == 1 then + mode = 2 + end + if biome == 3 then + if math.random(25) == 1 then + mode = 2 + else + mode = 1 + end + end + if biome == 4 or biome == 5 then + if math.random(3) == 1 then + mode = 2 + end + end + + local stalids = { + { {c_crystore, c_crystal}, {c_emore, c_emerald} }, + { {c_emore, c_emerald}, {c_crystore, c_crystal} }, + { {c_emore, c_emerald}, {c_meseore, c_mesecry} }, + { {c_ice, c_thinice}, {c_crystore, c_crystal}}, + { {c_ice, c_thinice}, {c_crystore, c_crystal}}, + { {c_rubore, c_ruby}, {c_meseore, c_mesecry}}, + { {c_crystore, c_crystal}, {c_rubore, c_ruby} }, + { {c_rubore, c_ruby}, {c_emore, c_emerald}}, + { {c_amethore, c_ameth}, {c_meseore, c_mesecry} }, + } + + local nid_a + local nid_b + local nid_s = c_stone --stone base, will be rewritten to ice in certain biomes + + if biome > 3 and biome < 6 then + if mode == 1 then + nid_a = c_ice + nid_b = c_thinice + nid_s = c_ice + else + nid_a = c_crystore + nid_b = c_crystal + end + elseif mode == 1 then + nid_a = stalids[biome][1][1] + nid_b = stalids[biome][1][2] + else + nid_a = stalids[biome][2][1] + nid_b = stalids[biome][2][2] + end + + local top = math.random(5,H_CRY) --grab a random height for the stalagmite + for j = 0, top do --y + for k = -3, 3 do + for l = -3, 3 do + if j == 0 then + if k*k + l*l <= 9 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = nid_s + end + elseif j <= top/5 then + if k*k + l*l <= 4 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = nid_a + end + elseif j <= top/5 * 3 then + if k*k + l*l <= 1 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = nid_b + end + else + local vi = area:index(x, y+j, z-3) + data[vi] = nid_b + end + end + end + end +end + +--crystal stalactite spawner +function caverealms:crystal_stalactite(x,y,z, area, data, biome) + + if not caverealms:above_solid(x,y,z,area,data) then + return + end + + --contest ids + local c_stone = minetest.get_content_id("default:stone") + local c_crystore = minetest.get_content_id("caverealms:glow_ore") + local c_crystal = minetest.get_content_id("caverealms:glow_crystal") + local c_emerald = minetest.get_content_id("caverealms:glow_emerald") + local c_emore = minetest.get_content_id("caverealms:glow_emerald_ore") + local c_mesecry = minetest.get_content_id("caverealms:glow_mese") + local c_meseore = minetest.get_content_id("default:stone_with_mese") + local c_ruby = minetest.get_content_id("caverealms:glow_ruby") + local c_rubore = minetest.get_content_id("caverealms:glow_ruby_ore") + local c_ameth = minetest.get_content_id("caverealms:glow_amethyst") + local c_amethore = minetest.get_content_id("caverealms:glow_amethyst_ore") + local c_ice = minetest.get_content_id("default:ice") + local c_thinice = minetest.get_content_id("caverealms:hanging_thin_ice") + + --for randomness + local mode = 1 + if math.random(15) == 1 then + mode = 2 + end + if biome == 3 then + if math.random(25) == 1 then + mode = 2 + else + mode = 1 + end + end + if biome == 4 or biome == 5 then + if math.random(3) == 1 then + mode = 2 + end + end + + local stalids = { + { {c_crystore, c_crystal}, {c_emore, c_emerald} }, + { {c_emore, c_emerald}, {c_crystore, c_crystal} }, + { {c_emore, c_emerald}, {c_meseore, c_mesecry} }, + { {c_ice, c_thinice}, {c_crystore, c_crystal}}, + { {c_ice, c_thinice}, {c_crystore, c_crystal}}, + { {c_rubore, c_ruby}, {c_meseore, c_mesecry}}, + { {c_crystore, c_crystal}, {c_rubore, c_ruby} }, + { {c_rubore, c_ruby}, {c_emore, c_emerald}}, + { {c_amethore, c_ameth}, {c_meseore, c_mesecry} }, + } + + local nid_a + local nid_b + local nid_s = c_stone --stone base, will be rewritten to ice in certain biomes + + if biome > 3 and biome < 6 then + if mode == 1 then + nid_a = c_ice + nid_b = c_thinice + nid_s = c_ice + else + nid_a = c_crystore + nid_b = c_crystal + end + elseif mode == 1 then + nid_a = stalids[biome][1][1] + nid_b = stalids[biome][1][2] + else + nid_a = stalids[biome][2][1] + nid_b = stalids[biome][2][2] + end + + local bot = math.random(-H_CLAC, -6) --grab a random height for the stalagmite + for j = bot, 0 do --y + for k = -3, 3 do + for l = -3, 3 do + if j >= -1 then + if k*k + l*l <= 9 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = nid_s + end + elseif j >= bot/5 then + if k*k + l*l <= 4 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = nid_a + end + elseif j >= bot/5 * 3 then + if k*k + l*l <= 1 then + local vi = area:index(x+k, y+j, z+l-3) + data[vi] = nid_b + end + else + local vi = area:index(x, y+j, z-3) + data[vi] = nid_b + end + end + end + end +end + +--glowing crystal stalagmite spawner +function caverealms:salt_stalagmite(x,y,z, area, data, biome) + + if not caverealms:below_solid(x,y,z,area,data) then + return + end + + --contest ids + local c_stone = minetest.get_content_id("default:stone") + local c_salt = minetest.get_content_id("caverealms:salt_crystal") + + local scale = math.random(2, 4) + if scale == 2 then + for j = -3, 3 do + for k = -3, 3 do + local vi = area:index(x+j, y, z+k) + data[vi] = c_stone + if math.abs(j) ~= 3 and math.abs(k) ~= 3 then + local vi = area:index(x+j, y+1, z+k) + data[vi] = c_stone + end + end + end + else + for j = -4, 4 do + for k = -4, 4 do + local vi = area:index(x+j, y, z+k) + data[vi] = c_stone + if math.abs(j) ~= 4 and math.abs(k) ~= 4 then + local vi = area:index(x+j, y+1, z+k) + data[vi] = c_stone + end + end + end + end + for j = 2, scale + 2 do --y + for k = -2, scale - 2 do + for l = -2, scale - 2 do + local vi = area:index(x+k, y+j, z+l) + data[vi] = c_salt -- make cube + end + end + end +end + +--function to create giant 'shrooms +function caverealms:giant_shroom(x, y, z, area, data) + + if not caverealms:below_solid(x,y,z,area,data) then + return + end + + local c_cap + local c_stem + + --as usual, grab the content ID's + if minetest.get_modpath("ethereal") then + c_stem = minetest.get_content_id("ethereal:mushroom_trunk") + c_cap = minetest.get_content_id("ethereal:mushroom") + else + c_stem = minetest.get_content_id("caverealms:mushroom_stem") + c_cap = minetest.get_content_id("caverealms:mushroom_cap") + end + + local c_gills = minetest.get_content_id("caverealms:mushroom_gills") + + z = z - 5 + --cap + for k = -5, 5 do + for l = -5, 5 do + if k*k + l*l <= 25 then + local vi = area:index(x+k, y+5, z+l) + data[vi] = c_cap + end + if k*k + l*l <= 16 then + local vi = area:index(x+k, y+6, z+l) + data[vi] = c_cap + vi = area:index(x+k, y+5, z+l) + data[vi] = c_gills + end + if k*k + l*l <= 9 then + local vi = area:index(x+k, y+7, z+l) + data[vi] = c_cap + end + if k*k + l*l <= 4 then + local vi = area:index(x+k, y+8, z+l) + data[vi] = c_cap + end + end + end + --stem + for j = 0, 5 do + for k = -1,1 do + local vi = area:index(x+k, y+j, z) + data[vi] = c_stem + if k == 0 then + local ai = area:index(x, y+j, z+1) + data[ai] = c_stem + ai = area:index(x, y+j, z-1) + data[ai] = c_stem + end + end + end +end diff --git a/mods/.caverealms_old/init.lua b/mods/.caverealms_old/init.lua new file mode 100644 index 00000000..b40c6bbb --- /dev/null +++ b/mods/.caverealms_old/init.lua @@ -0,0 +1,24 @@ +-- caverealms v.0.8 by HeroOfTheWinds +-- original cave code modified from paramat's subterrain +-- For Minetest 0.4.8 stable +-- Depends default +-- License: code WTFPL + +caverealms = {} --create a container for functions and constants + +--grab a shorthand for the filepath of the mod +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +--load companion lua files +dofile(modpath.."/config.lua") --configuration file; holds various constants +dofile(modpath.."/crafting.lua") --crafting recipes +dofile(modpath.."/nodes.lua") --node definitions +dofile(modpath.."/functions.lua") --function definitions +dofile(modpath.."/plants.lua") +dofile(modpath.."/biomes.lua") + +if minetest.get_modpath("mobs_monster") then + if caverealms.config.dm_spawn == true then + dofile(modpath.."/dungeon_master.lua") --special DMs for DM's Lair biome + end +end \ No newline at end of file diff --git a/mods/.caverealms_old/mod.conf b/mods/.caverealms_old/mod.conf new file mode 100644 index 00000000..1f32f704 --- /dev/null +++ b/mods/.caverealms_old/mod.conf @@ -0,0 +1 @@ +name = caverealms \ No newline at end of file diff --git a/mods/.caverealms_old/nodes.lua b/mods/.caverealms_old/nodes.lua new file mode 100644 index 00000000..8057a334 --- /dev/null +++ b/mods/.caverealms_old/nodes.lua @@ -0,0 +1,521 @@ +-- CaveRealms nodes.lua + +--NODES-- + +--glowing crystal +minetest.register_node("caverealms:glow_crystal", { + description = "Glow Sapphire", + tiles = {"caverealms_glow_crystal.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + light_source = 13, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + +--glowing emerald +minetest.register_node("caverealms:glow_emerald", { + description = "Glow Emerald", + tiles = {"caverealms_glow_emerald.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + light_source = 13, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + +--glowing mese crystal blocks +minetest.register_node("caverealms:glow_mese", { + description = "Glow Mese Crystal", + tiles = {"caverealms_glow_mese.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + light_source = 13, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + +--glowing ruby +minetest.register_node("caverealms:glow_ruby", { + description = "Glow Ruby", + tiles = {"caverealms_glow_ruby.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + light_source = 13, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + +--glowing amethyst +minetest.register_node("caverealms:glow_amethyst", { + description = "Glow Amethyst", + tiles = {"caverealms_glow_amethyst.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + light_source = 13, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + +--embedded crystal +minetest.register_node("caverealms:glow_ore", { + description = "Glow Crystal Ore", + tiles = {"caverealms_glow_ore.png"}, + is_ground_content = true, + groups = {cracky=2}, + sounds = default.node_sound_glass_defaults(), + light_source = 10, + paramtype = "light", +}) + +--embedded emerald +minetest.register_node("caverealms:glow_emerald_ore", { + description = "Glow Emerald Ore", + tiles = {"caverealms_glow_emerald_ore.png"}, + is_ground_content = true, + groups = {cracky=2}, + sounds = default.node_sound_glass_defaults(), + light_source = 10, + paramtype = "light", +}) + +--embedded ruby +minetest.register_node("caverealms:glow_ruby_ore", { + description = "Glow Ruby Ore", + tiles = {"caverealms_glow_ruby_ore.png"}, + is_ground_content = true, + groups = {cracky=2}, + sounds = default.node_sound_glass_defaults(), + light_source = 10, + paramtype = "light", +}) + +--embedded amethyst +minetest.register_node("caverealms:glow_amethyst_ore", { + description = "Glow Amethyst Ore", + tiles = {"caverealms_glow_amethyst_ore.png"}, + is_ground_content = true, + groups = {cracky=2}, + sounds = default.node_sound_glass_defaults(), + light_source = 10, + paramtype = "light", +}) + +--thin (transparent) ice +minetest.register_node("caverealms:thin_ice", { + description = "Thin Ice", + tiles = {"caverealms_thin_ice.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, + freezemelt = "default:water_source", + paramtype = "light", +}) + +--salt crystal +minetest.register_node("caverealms:salt_crystal", { + description = "Salt Crystal", + tiles = {"caverealms_salt_crystal.png"}, + is_ground_content = true, + groups = {cracky=2}, + sounds = default.node_sound_glass_defaults(), + light_source = 11, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + +--glowing crystal gem +minetest.register_node("caverealms:glow_gem", { + description = "Glow Gem", + tiles = {"caverealms_glow_gem.png"}, + inventory_image = "caverealms_glow_gem.png", + wield_image = "caverealms_glow_gem.png", + is_ground_content = true, + groups = {cracky = 3, oddly_breakable_by_hand = 1, attached_node = 1}, + sounds = default.node_sound_glass_defaults(), + light_source = 11, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 0.75, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + } +}) + +--glowing salt gem +minetest.register_node("caverealms:salt_gem", { + description = "Salt Gem", + tiles = {"caverealms_salt_gem.png"}, + inventory_image = "caverealms_salt_gem.png", + wield_image = "caverealms_salt_gem.png", + is_ground_content = true, + groups = {cracky = 3, oddly_breakable_by_hand = 1, attached_node = 1}, + sounds = default.node_sound_glass_defaults(), + light_source = 11, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 0.75, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + } +}) + +--stone spike +minetest.register_node("caverealms:spike", { + description = "Stone Spike", + tiles = {"caverealms_spike.png"}, + inventory_image = "caverealms_spike.png", + wield_image = "caverealms_spike.png", + is_ground_content = true, + groups = {cracky = 3, oddly_breakable_by_hand = 1, attached_node = 1}, + sounds = default.node_sound_stone_defaults(), + light_source = 3, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 0.75, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + } +}) + + +--upward pointing icicle +minetest.register_node("caverealms:icicle_up", { + description = "Icicle", + tiles = {"caverealms_icicle_up.png"}, + inventory_image = "caverealms_icicle_up.png", + wield_image = "caverealms_icicle_up.png", + is_ground_content = true, + groups = {cracky=3, oddly_breakable_by_hand=1}, + sounds = default.node_sound_glass_defaults(), + light_source = 8, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + }, +}) + +--downward pointing icicle +minetest.register_node("caverealms:icicle_down", { + description = "Icicle", + tiles = {"caverealms_icicle_down.png"}, + inventory_image = "caverealms_icicle_down.png", + wield_image = "caverealms_icicle_down.png", + is_ground_content = true, + groups = {cracky=3, oddly_breakable_by_hand=1}, + sounds = default.node_sound_glass_defaults(), + light_source = 8, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + }, +}) + +--cave mossy cobble - bluish? +minetest.register_node("caverealms:stone_with_moss", { + description = "Cave Stone with Moss", + tiles = {"default_cobble.png^caverealms_moss.png", "default_cobble.png", "default_cobble.png^caverealms_moss_side.png"}, + is_ground_content = true, + groups = {crumbly=1, cracky=3}, + drop = 'default:cobble', + sounds = default.node_sound_dirt_defaults({ + footstep = {name="default_grass_footstep", gain=0.25}, + }), +}) + +--cave lichen-covered cobble - purple-ish +minetest.register_node("caverealms:stone_with_lichen", { + description = "Cave Stone with Lichen", + tiles = {"default_cobble.png^caverealms_lichen.png", "default_cobble.png", "default_cobble.png^caverealms_lichen_side.png"}, + is_ground_content = true, + groups = {crumbly=1, cracky=3}, + drop = 'default:cobble', + sounds = default.node_sound_dirt_defaults({ + footstep = {name="default_grass_footstep", gain=0.25}, + }), +}) + +--cave algae-covered cobble - yellow-ish +minetest.register_node("caverealms:stone_with_algae", { + description = "Cave Stone with Algae", + tiles = {"default_cobble.png^caverealms_algae.png", "default_cobble.png", "default_cobble.png^caverealms_algae_side.png"}, + is_ground_content = true, + groups = {crumbly=1, cracky=3}, + drop = 'default:cobble', + sounds = default.node_sound_dirt_defaults({ + footstep = {name="default_grass_footstep", gain=0.25}, + }), +}) + +--tiny-salt-crystal-covered cobble - pink-ish +minetest.register_node("caverealms:stone_with_salt", { + description = "Salt Crystal", + tiles = {"caverealms_salty2.png"}, + light_source = 9, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), +}) + +--Hot Cobble - cobble with lava instead of mortar XD +minetest.register_node("caverealms:hot_cobble", { + description = "Hot Cobble", + tiles = {"caverealms_hot_cobble.png"}, + is_ground_content = true, + groups = {cracky=1, hot=1, unbreakable = 1, stone = 1}, + damage_per_second = 1, + light_source = 3, + paramtype = "light", + sounds = default.node_sound_stone_defaults(), +}) + +--Glow Obsidian +minetest.register_node("caverealms:glow_obsidian", { + description = "Glowing Obsidian", + tiles = {"caverealms_glow_obsidian.png"}, + is_ground_content = true, + groups = {cracky=1, level=2}, + light_source = 7, + paramtype = "light", + sounds = default.node_sound_stone_defaults(), +}) + +--Glow Obsidian 2 - has traces of lava +minetest.register_node("caverealms:glow_obsidian_2", { + description = "Hot Glowing Obsidian", + tiles = {"caverealms_glow_obsidian2.png"}, + is_ground_content = true, + groups = {cracky=1, hot=1, level=2}, + light_source = 9, + paramtype = "light", + sounds = default.node_sound_stone_defaults(), +}) + +--Glow Obsidian Bricks +minetest.register_node("caverealms:glow_obsidian_brick", { + description = "Glow Obsidian Brick", + tiles = {"caverealms_glow_obsidian_brick.png"}, + light_source = 7, + groups = {cracky = 1, level = 2}, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("caverealms:glow_obsidian_brick_2", { + description = "Glow Obsidian Brick", + tiles = {"caverealms_glow_obsidian_brick_2.png"}, + light_source = 9, + groups = {cracky = 1, level = 2}, + sounds = default.node_sound_stone_defaults(), +}) + +--Glow Obsidian Stairs/Slabs +stairs.register_stair_and_slab( + "glow_obsidian_brick", + "caverealms:glow_obsidian_brick", + {cracky = 1, level = 2}, + {"caverealms_glow_obsidian_brick.png"}, + "Glow Obsidian Brick Stair", + "Glow Obsidian Brick Slab", + default.node_sound_stone_defaults()) + +stairs.register_stair_and_slab( + "glow_obsidian_brick_2", + "caverealms:glow_obsidian_brick_2", + {cracky = 1, level = 2}, + {"caverealms_glow_obsidian_brick_2.png"}, + "Glow Obsidian Brick Stair", + "Glow Obsidian Brick Slab", + default.node_sound_stone_defaults()) + +--Glow Obsidian Glass +minetest.register_node("caverealms:glow_obsidian_glass", { + description = "Glow Obsidian Glass", + drawtype = "glasslike_framed_optional", + tiles = {"caverealms_glow_obsidian_glass.png", "default_obsidian_glass_detail.png"}, + paramtype = "light", + light_source = 13, + sunlight_propagates = true, + groups = {cracky = 3}, + sounds = default.node_sound_glass_defaults(), +}) + +--Coal Dust +minetest.register_node("caverealms:coal_dust", { + description = "Coal Dust", + tiles = {"caverealms_coal_dust.png"}, + is_ground_content = true, + groups = {crumbly=3, falling_node=1, sand=1}, + sounds = default.node_sound_sand_defaults(), +}) + +--glow worms +minetest.register_node("caverealms:glow_worm", { + description = "Blue Glow Worms", + tiles = {"caverealms_glow_worm.png"}, + inventory_image = "caverealms_glow_worm.png", + wield_image = "caverealms_glow_worm.png", + is_ground_content = true, + groups = {oddly_breakable_by_hand=3}, + light_source = 9, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-1/6, -1/2, -1/6, 1/6, 1/2, 1/6}, + }, +}) + +minetest.register_node("caverealms:glow_worm_green", { + description = "Green Glow Worms", + tiles = {"caverealms_glow_worm_green.png"}, + inventory_image = "caverealms_glow_worm_green.png", + wield_image = "caverealms_glow_worm_green.png", + is_ground_content = true, + groups = {oddly_breakable_by_hand=3}, + light_source = 9, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-1/6, -1/2, -1/6, 1/6, 1/2, 1/6}, + }, +}) + +minetest.register_node("caverealms:fire_vine", { + description = "Fire Vine", + tiles = {"caverealms_fire_vine.png"}, + inventory_image = "caverealms_fire_vine.png", + wield_image = "caverealms_fire_vine.png", + is_ground_content = true, + damage_per_second = 1, + groups = {oddly_breakable_by_hand=3}, + light_source = 9, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-1/6, -1/2, -1/6, 1/6, 1/2, 1/6}, + }, +}) + + +--define special flame so that it does not expire +minetest.register_node("caverealms:constant_flame", { + description = "Fire", + drawtype = "plantlike", + tiles = {{ + name="fire_basic_flame_animated.png", + animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}, + }}, + inventory_image = "fire_basic_flame.png", + light_source = 14, + groups = {igniter=2, dig_immediate=3, hot=3, not_in_creative_inventory=1}, + paramtype = "light", + drop = '', + walkable = false, + buildable_to = true, + floodable = true, + damage_per_second = 4, +}) + +--dungeon master statue (nodebox) +minetest.register_node("caverealms:dm_statue", { + description = "Dungeon Master Statue", + tiles = { + "caverealms_dm_stone.png", + "caverealms_dm_stone.png", + "caverealms_dm_stone.png", + "caverealms_dm_stone.png", + "caverealms_dm_stone.png", + "caverealms_stone_eyes.png" + }, + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + groups = {cracky=2}, + node_box = { + type = "fixed", + fixed = { + {-0.4375, -0.5, -0.4375, 0.4375, -0.3125, 0.4375}, -- NodeBox1 + {-0.25, -0.125, -0.1875, 0.25, 0.5, 0.1875}, -- NodeBox2 + {-0.375, 0, -0.125, -0.25, 0.4375, 0.125}, -- NodeBox3 + {0.25, 0.125, -0.4375, 0.375, 0.375, 0.1875}, -- NodeBox4 + {-0.25, -0.5, -0.125, -0.125, -0.125, 0.125}, -- NodeBox5 + {0.125, -0.3125, -0.125, 0.25, 0, 0.125}, -- NodeBox6 + } + }, + selection_box = { + type = "regular" + } +}) + + +-- Compatibility +minetest.register_alias("caverealms:hanging_thin_ice", "caverealms:thin_ice") + +minetest.register_alias("caverealms:spike_2", "caverealms:spike") +minetest.register_alias("caverealms:spike_3", "caverealms:spike") +minetest.register_alias("caverealms:spike_4", "caverealms:spike") +minetest.register_alias("caverealms:spike_5", "caverealms:spike") + +minetest.register_alias("caverealms:salt_gem_2", "caverealms:salt_gem") +minetest.register_alias("caverealms:salt_gem_3", "caverealms:salt_gem") +minetest.register_alias("caverealms:salt_gem_4", "caverealms:salt_gem") +minetest.register_alias("caverealms:salt_gem_5", "caverealms:salt_gem") + +minetest.register_alias("caverealms:glow_gem_2", "caverealms:glow_gem") +minetest.register_alias("caverealms:glow_gem_3", "caverealms:glow_gem") +minetest.register_alias("caverealms:glow_gem_4", "caverealms:glow_gem") +minetest.register_alias("caverealms:glow_gem_5", "caverealms:glow_gem") diff --git a/mods/.caverealms_old/plants.lua b/mods/.caverealms_old/plants.lua new file mode 100644 index 00000000..60c65698 --- /dev/null +++ b/mods/.caverealms_old/plants.lua @@ -0,0 +1,191 @@ +-- Cavrealms plants and trees for lichen, moss and algae biomes + +-- Lichen biome + +-- glowing fungi +minetest.register_node("caverealms:fungus", { + description = "Glowing Fungus", + tiles = {"caverealms_fungi.png"}, + inventory_image = "caverealms_fungi.png", + wield_image = "caverealms_fungi.png", + is_ground_content = true, + groups = {oddly_breakable_by_hand = 3, attached_node = 1, flammable = 1}, + light_source = 5, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + }, +}) + +-- mycena mushroom +minetest.register_node("caverealms:mycena", { + description = "Mycena Mushroom", + tiles = {"caverealms_mycena.png"}, + inventory_image = "caverealms_mycena.png", + wield_image = "caverealms_mycena.png", + is_ground_content = true, + groups = {oddly_breakable_by_hand = 3, attached_node = 1, flammable = 1}, + light_source = 6, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + }, +}) + +-- giant mushroom +if minetest.get_modpath("ethereal") then + minetest.register_alias("caverealms:mushroom_cap", "ethereal:mushroom") + minetest.register_alias("caverealms:mushroom_stem", "ethereal:mushroom_trunk") +else + -- stem + minetest.register_node("caverealms:mushroom_stem", { + description = "Giant Mushroom Stem", + tiles = {"caverealms_mushroom_stem.png"}, + is_ground_content = true, + groups = {choppy=2, oddly_breakable_by_hand=1}, + }) + + -- cap + minetest.register_node("caverealms:mushroom_cap", { + description = "Giant Mushroom Cap", + tiles = {"caverealms_mushroom_cap.png"}, + is_ground_content = true, + groups = {choppy=2, oddly_breakable_by_hand=1,}, + drop = { + max_items = 1, + items = { + {items = {"caverealms:mushroom_sapling"}, rarity = 20}, + {items = {"caverealms:mushroom_cap"}} + } + }, + }) + + -- sapling + minetest.register_node("caverealms:mushroom_sapling", { + description = "Mushroom Tree Sapling", + drawtype = "plantlike", + tiles = {"caverealms_mushroom_sapling.png"}, + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + walkable = false, + selection_box = { + type = "fixed", + fixed = {-4 / 16, -0.5, -4 / 16, 4 / 16, 7 / 16, 4 / 16} + }, + groups = {snappy = 2, dig_immediate = 3, flammable = 2}, + sounds = default.node_sound_leaves_defaults(), + }) +end + +-- gills +minetest.register_node("caverealms:mushroom_gills", { + description = "Giant Mushroom Gills", + tiles = {"caverealms_mushroom_gills.png"}, + is_ground_content = true, + light_source = 10, + walkable = false, + groups = {choppy=2, oddly_breakable_by_hand=1}, + drawtype = "plantlike", + paramtype = "light", +}) + + +-- Saplings + +-- grow trees +local add_tree = function (pos, ofx, ofy, ofz, schem) + if not schem then + print ("Schematic not found") + return + end + minetest.swap_node(pos, {name = "air"}) + minetest.place_schematic( + {x = pos.x - ofx, y = pos.y - ofy, z = pos.z - ofz}, + schem, 0, nil, false) +end + +local path = minetest.get_modpath("caverealms").."/schematics/" + +-- giant mushrooms +function grow_caverealms_mushroom(pos) + add_tree(pos, 5, 0, 5, path .. "shroom.mts") +end + +-- height check +local function enough_height(pos, height) + local nod = minetest.line_of_sight( + {x = pos.x, y = pos.y + 1, z = pos.z}, + {x = pos.x, y = pos.y + height, z = pos.z}) + if not nod then + return false + else + return true + end +end + +minetest.register_abm({ + label = "Caverealms grow sapling", + nodenames = {"ethereal:mushroom_sapling", "caverealms:mushroom_sapling"}, + interval = 10, + chance = 50, + catch_up = false, + action = function(pos, node) + local light_level = minetest.get_node_light(pos) + -- check light level + if not light_level or light_level > 10 then + return + end + -- get node under sapling + local under = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name + -- check if registered + if not minetest.registered_nodes[node.name] then + return + end + -- ethereal sapling on lichen stone + if node.name == "ethereal:mushroom_sapling" + and under == "caverealms:stone_with_lichen" + and enough_height(pos, 10) then + grow_caverealms_mushroom(pos) + -- caverealms sapling on lichen stone + elseif node.name == "caverealms:mushroom_sapling" + and under == "caverealms:stone_with_lichen" + and enough_height(pos, 10) then + grow_caverealms_mushroom(pos) + end + end, +}) + + +-- spread moss/lichen/algae to nearby cobblestone +minetest.register_abm({ + label = "Caverealms stone spread", + nodenames = { + "caverealms:stone_with_moss", + "caverealms:stone_with_lichen", + "caverealms:stone_with_algae", + }, + neighbors = {"air"}, + interval = 16, + chance = 50, + catch_up = false, + action = function(pos, node) + local num = minetest.find_nodes_in_area_under_air( + {x = pos.x - 1, y = pos.y - 2, z = pos.z - 1}, + {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, + "default:cobble") + if #num > 0 then + minetest.set_node(num[math.random(#num)], {name = node.name}) + end + end, +}) diff --git a/mods/caverealms/schematics/amethyst_stalactite.mts b/mods/.caverealms_old/schematics/amethyst_stalactite.mts similarity index 100% rename from mods/caverealms/schematics/amethyst_stalactite.mts rename to mods/.caverealms_old/schematics/amethyst_stalactite.mts diff --git a/mods/caverealms/schematics/amethyst_stalagmite.mts b/mods/.caverealms_old/schematics/amethyst_stalagmite.mts similarity index 100% rename from mods/caverealms/schematics/amethyst_stalagmite.mts rename to mods/.caverealms_old/schematics/amethyst_stalagmite.mts diff --git a/mods/caverealms/schematics/emerald_stalactite.mts b/mods/.caverealms_old/schematics/emerald_stalactite.mts similarity index 100% rename from mods/caverealms/schematics/emerald_stalactite.mts rename to mods/.caverealms_old/schematics/emerald_stalactite.mts diff --git a/mods/caverealms/schematics/emerald_stalagmite.mts b/mods/.caverealms_old/schematics/emerald_stalagmite.mts similarity index 100% rename from mods/caverealms/schematics/emerald_stalagmite.mts rename to mods/.caverealms_old/schematics/emerald_stalagmite.mts diff --git a/mods/caverealms/schematics/ruby_stalactite.mts b/mods/.caverealms_old/schematics/ruby_stalactite.mts similarity index 100% rename from mods/caverealms/schematics/ruby_stalactite.mts rename to mods/.caverealms_old/schematics/ruby_stalactite.mts diff --git a/mods/caverealms/schematics/ruby_stalagmite.mts b/mods/.caverealms_old/schematics/ruby_stalagmite.mts similarity index 100% rename from mods/caverealms/schematics/ruby_stalagmite.mts rename to mods/.caverealms_old/schematics/ruby_stalagmite.mts diff --git a/mods/caverealms/schematics/sapphire_stalactite.mts b/mods/.caverealms_old/schematics/sapphire_stalactite.mts similarity index 100% rename from mods/caverealms/schematics/sapphire_stalactite.mts rename to mods/.caverealms_old/schematics/sapphire_stalactite.mts diff --git a/mods/caverealms/schematics/sapphire_stalagmite.mts b/mods/.caverealms_old/schematics/sapphire_stalagmite.mts similarity index 100% rename from mods/caverealms/schematics/sapphire_stalagmite.mts rename to mods/.caverealms_old/schematics/sapphire_stalagmite.mts diff --git a/mods/.caverealms_old/schematics/shroom.mts b/mods/.caverealms_old/schematics/shroom.mts new file mode 100644 index 00000000..07e49668 Binary files /dev/null and b/mods/.caverealms_old/schematics/shroom.mts differ diff --git a/mods/caverealms/textures/Thumbs.db b/mods/.caverealms_old/textures/Thumbs.db similarity index 100% rename from mods/caverealms/textures/Thumbs.db rename to mods/.caverealms_old/textures/Thumbs.db diff --git a/mods/.caverealms_old/textures/caverealms_algae.png b/mods/.caverealms_old/textures/caverealms_algae.png new file mode 100644 index 00000000..1331bb03 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_algae.png differ diff --git a/mods/.caverealms_old/textures/caverealms_algae_side.png b/mods/.caverealms_old/textures/caverealms_algae_side.png new file mode 100644 index 00000000..b9bd4ed0 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_algae_side.png differ diff --git a/mods/.caverealms_old/textures/caverealms_angler_rod.png b/mods/.caverealms_old/textures/caverealms_angler_rod.png new file mode 100644 index 00000000..799abec4 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_angler_rod.png differ diff --git a/mods/.caverealms_old/textures/caverealms_angler_rod_baited.png b/mods/.caverealms_old/textures/caverealms_angler_rod_baited.png new file mode 100644 index 00000000..98c9f3ba Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_angler_rod_baited.png differ diff --git a/mods/.caverealms_old/textures/caverealms_angler_rod_weild.png b/mods/.caverealms_old/textures/caverealms_angler_rod_weild.png new file mode 100644 index 00000000..160a5fff Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_angler_rod_weild.png differ diff --git a/mods/.caverealms_old/textures/caverealms_coal_dust.png b/mods/.caverealms_old/textures/caverealms_coal_dust.png new file mode 100644 index 00000000..be6cbbbb Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_coal_dust.png differ diff --git a/mods/.caverealms_old/textures/caverealms_dm_stone.png b/mods/.caverealms_old/textures/caverealms_dm_stone.png new file mode 100644 index 00000000..73e92664 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_dm_stone.png differ diff --git a/mods/.caverealms_old/textures/caverealms_fire_vine.png b/mods/.caverealms_old/textures/caverealms_fire_vine.png new file mode 100644 index 00000000..6f2c2a34 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_fire_vine.png differ diff --git a/mods/.caverealms_old/textures/caverealms_fungi.png b/mods/.caverealms_old/textures/caverealms_fungi.png new file mode 100644 index 00000000..fde8cbfd Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_fungi.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_amethyst.png b/mods/.caverealms_old/textures/caverealms_glow_amethyst.png new file mode 100644 index 00000000..7daa61bb Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_amethyst.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_amethyst_ore.png b/mods/.caverealms_old/textures/caverealms_glow_amethyst_ore.png new file mode 100644 index 00000000..403d907b Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_amethyst_ore.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_bait.png b/mods/.caverealms_old/textures/caverealms_glow_bait.png new file mode 100644 index 00000000..56a084b5 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_bait.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_crystal.png b/mods/.caverealms_old/textures/caverealms_glow_crystal.png new file mode 100644 index 00000000..b9be3951 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_crystal.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_emerald.png b/mods/.caverealms_old/textures/caverealms_glow_emerald.png new file mode 100644 index 00000000..025a48b0 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_emerald.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_emerald_ore.png b/mods/.caverealms_old/textures/caverealms_glow_emerald_ore.png new file mode 100644 index 00000000..9f601aa4 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_emerald_ore.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_gem.png b/mods/.caverealms_old/textures/caverealms_glow_gem.png new file mode 100644 index 00000000..9bf3426a Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_gem.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_mese.png b/mods/.caverealms_old/textures/caverealms_glow_mese.png new file mode 100644 index 00000000..ed433fed Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_mese.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_obsidian.png b/mods/.caverealms_old/textures/caverealms_glow_obsidian.png new file mode 100644 index 00000000..a1577382 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_obsidian.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_obsidian2.png b/mods/.caverealms_old/textures/caverealms_glow_obsidian2.png new file mode 100644 index 00000000..7d1dcf38 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_obsidian2.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_obsidian_brick.png b/mods/.caverealms_old/textures/caverealms_glow_obsidian_brick.png new file mode 100644 index 00000000..a5c6db82 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_obsidian_brick.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_obsidian_brick_2.png b/mods/.caverealms_old/textures/caverealms_glow_obsidian_brick_2.png new file mode 100644 index 00000000..da2b2e33 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_obsidian_brick_2.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_obsidian_glass.png b/mods/.caverealms_old/textures/caverealms_glow_obsidian_glass.png new file mode 100644 index 00000000..afd0fa7e Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_obsidian_glass.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_ore.png b/mods/.caverealms_old/textures/caverealms_glow_ore.png new file mode 100644 index 00000000..19179a9c Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_ore.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_ruby.png b/mods/.caverealms_old/textures/caverealms_glow_ruby.png new file mode 100644 index 00000000..59fd46b6 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_ruby.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_ruby_ore.png b/mods/.caverealms_old/textures/caverealms_glow_ruby_ore.png new file mode 100644 index 00000000..31ecb63b Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_ruby_ore.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_worm.png b/mods/.caverealms_old/textures/caverealms_glow_worm.png new file mode 100644 index 00000000..e324fbd6 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_worm.png differ diff --git a/mods/.caverealms_old/textures/caverealms_glow_worm_green.png b/mods/.caverealms_old/textures/caverealms_glow_worm_green.png new file mode 100644 index 00000000..686c2779 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_glow_worm_green.png differ diff --git a/mods/.caverealms_old/textures/caverealms_hot_cobble.png b/mods/.caverealms_old/textures/caverealms_hot_cobble.png new file mode 100644 index 00000000..2e873e84 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_hot_cobble.png differ diff --git a/mods/.caverealms_old/textures/caverealms_icicle_down.png b/mods/.caverealms_old/textures/caverealms_icicle_down.png new file mode 100644 index 00000000..adcc340b Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_icicle_down.png differ diff --git a/mods/.caverealms_old/textures/caverealms_icicle_up.png b/mods/.caverealms_old/textures/caverealms_icicle_up.png new file mode 100644 index 00000000..fcd5e2a2 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_icicle_up.png differ diff --git a/mods/.caverealms_old/textures/caverealms_lichen.png b/mods/.caverealms_old/textures/caverealms_lichen.png new file mode 100644 index 00000000..f38fe767 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_lichen.png differ diff --git a/mods/.caverealms_old/textures/caverealms_lichen_side.png b/mods/.caverealms_old/textures/caverealms_lichen_side.png new file mode 100644 index 00000000..b036dc3f Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_lichen_side.png differ diff --git a/mods/.caverealms_old/textures/caverealms_moss.png b/mods/.caverealms_old/textures/caverealms_moss.png new file mode 100644 index 00000000..fd23ec37 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_moss.png differ diff --git a/mods/.caverealms_old/textures/caverealms_moss_side.png b/mods/.caverealms_old/textures/caverealms_moss_side.png new file mode 100644 index 00000000..a16d64ab Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_moss_side.png differ diff --git a/mods/.caverealms_old/textures/caverealms_mushroom_cap.png b/mods/.caverealms_old/textures/caverealms_mushroom_cap.png new file mode 100644 index 00000000..e5a1a35a Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_mushroom_cap.png differ diff --git a/mods/.caverealms_old/textures/caverealms_mushroom_gills.png b/mods/.caverealms_old/textures/caverealms_mushroom_gills.png new file mode 100644 index 00000000..596de9ec Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_mushroom_gills.png differ diff --git a/mods/.caverealms_old/textures/caverealms_mushroom_sapling.png b/mods/.caverealms_old/textures/caverealms_mushroom_sapling.png new file mode 100644 index 00000000..262c6900 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_mushroom_sapling.png differ diff --git a/mods/.caverealms_old/textures/caverealms_mushroom_stem.png b/mods/.caverealms_old/textures/caverealms_mushroom_stem.png new file mode 100644 index 00000000..d329ef12 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_mushroom_stem.png differ diff --git a/mods/.caverealms_old/textures/caverealms_mycena.png b/mods/.caverealms_old/textures/caverealms_mycena.png new file mode 100644 index 00000000..50ea65ba Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_mycena.png differ diff --git a/mods/.caverealms_old/textures/caverealms_salt_crystal.png b/mods/.caverealms_old/textures/caverealms_salt_crystal.png new file mode 100644 index 00000000..dde96fc2 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_salt_crystal.png differ diff --git a/mods/.caverealms_old/textures/caverealms_salt_gem.png b/mods/.caverealms_old/textures/caverealms_salt_gem.png new file mode 100644 index 00000000..0419e2ef Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_salt_gem.png differ diff --git a/mods/.caverealms_old/textures/caverealms_salty2.png b/mods/.caverealms_old/textures/caverealms_salty2.png new file mode 100644 index 00000000..7f8b5219 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_salty2.png differ diff --git a/mods/.caverealms_old/textures/caverealms_spike.png b/mods/.caverealms_old/textures/caverealms_spike.png new file mode 100644 index 00000000..253c9e6e Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_spike.png differ diff --git a/mods/.caverealms_old/textures/caverealms_stone_eyes.png b/mods/.caverealms_old/textures/caverealms_stone_eyes.png new file mode 100644 index 00000000..288db067 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_stone_eyes.png differ diff --git a/mods/.caverealms_old/textures/caverealms_thin_ice.png b/mods/.caverealms_old/textures/caverealms_thin_ice.png new file mode 100644 index 00000000..b16036d9 Binary files /dev/null and b/mods/.caverealms_old/textures/caverealms_thin_ice.png differ diff --git a/mods/.caverealms_old/textures/fire_basic_flame.png b/mods/.caverealms_old/textures/fire_basic_flame.png new file mode 100644 index 00000000..7a126e32 Binary files /dev/null and b/mods/.caverealms_old/textures/fire_basic_flame.png differ diff --git a/mods/.caverealms_old/textures/fire_basic_flame_animated.png b/mods/.caverealms_old/textures/fire_basic_flame_animated.png new file mode 100644 index 00000000..3b312e53 Binary files /dev/null and b/mods/.caverealms_old/textures/fire_basic_flame_animated.png differ diff --git a/mods/airutils/.luacheckrc b/mods/airutils/.luacheckrc new file mode 100644 index 00000000..14d4d8ad --- /dev/null +++ b/mods/airutils/.luacheckrc @@ -0,0 +1,46 @@ +-- Disable some (non critical) warnings +-- unused = false +unused_args = false +redefined = false + +globals = { + "airutils", +} + +read_globals = { + "DIR_DELIM", + "ItemStack", + "PseudoRandom", + "basic_machines", + "biomass", + "climate_api", + "core", + "creative", + "default", + "dump", + "emote", + "math", + "mcl_formspec", + "mcl_player", + "minetest", + "player_api", + "signs_lib", + "skins", + "string", + "technic", + "vector", + "wardrobe", +} + +-- Per file options +files["airutils_biofuel.lua"] = { + globals = {"basic_machines.grinder_recipes"}, +} + +files["lib_planes/utilities.lua"] = { + globals = {"player_api.player_attached.?", "mcl_player.player_attached.?"} +} + +files["pilot_skin_manager.lua"] = { + globals = {"skins.skin_class.apply_skin_to_player"} +} \ No newline at end of file diff --git a/mods/airutils/LICENSE b/mods/airutils/LICENSE new file mode 100644 index 00000000..a384c764 --- /dev/null +++ b/mods/airutils/LICENSE @@ -0,0 +1,37 @@ +Except for the file "text.lua", adapted from signs_lib from VanessaE, +that is licenced under LGPL 3.0 (see at: https://www.gnu.org/licenses/lgpl-3.0.txt) +all the code is licenced under MIT Licence, as below: + +================================================================================================== + +MIT License + +Copyright (c) 2024 APercy - Alexsandro Percy +Copyright (c) 2019 TheTermos (for code from mobkit at physics_lib.lua) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +================================================================================================== + +Media Licence: + +CC0-1.0 for all media +the "airutils_explode.ogg" comes from minetest game tnt mod + + diff --git a/mods/airutils/README.md b/mods/airutils/README.md new file mode 100644 index 00000000..2fa57121 --- /dev/null +++ b/mods/airutils/README.md @@ -0,0 +1,16 @@ +# airutils +Airport Utils for Minetest + +This mod is a lib and offers utilities to be used in airports. +We have only 2 items now, but will be expanded as needed. + +The first is PAPI (Precision Approach Path Indicator), to guide you in approaching landings. I recommend this article from wikipedia to understand how it works: https://en.wikipedia.org/wiki/Precision ... _indicator + +The PAPI is usually located on the left-hand side of the runway at right angles to the runway center line, so, if you want to place it at right side, just right click it to invert lights order. + + +The second, the tug, allows the repositioning of aircraft that may be blocking the runway or taxiways. In my mods only the owner can fly his own plane, so with this tool, if the person owns the area or the area has no owner, it is possible to move the aircraft without the presence of the pilot. Just click to move, and shift + click to rotate in your direction + +Except the media listed bellow, all media licence is CC0 +pilot_novaskin_girl.png adapted from Pilot Girl - https://minecraft.novaskin.me/skin/254193054/Pilot-girl +pilot_novaskin_girl_steampunk.png from girl steampunk pilot - https://minecraft.novaskin.me/skin/6190203356577792/girl-steampunk-pilot diff --git a/mods/airutils/airutils_biofuel.lua b/mods/airutils/airutils_biofuel.lua new file mode 100644 index 00000000..9bf05e72 --- /dev/null +++ b/mods/airutils/airutils_biofuel.lua @@ -0,0 +1,267 @@ +---------- +--biofuel +---------- +local S = airutils.S +local module_name = "airutils" + +if core.get_modpath("technic") then + if technic then + technic.register_extractor_recipe({input = {"farming:wheat 33"}, output = "biofuel:biofuel 1"}) + technic.register_extractor_recipe({input = {"farming:corn 33"}, output = "biofuel:biofuel 1"}) + technic.register_extractor_recipe({input = {"farming:potato 33"}, output = "biofuel:biofuel 1"}) + technic.register_extractor_recipe({input = {"default:papyrus 99"}, output = "biofuel:biofuel 1"}) + end +end + + +if core.get_modpath("basic_machines") then + if basic_machines then + basic_machines.grinder_recipes["farming:wheat"] = {50,"biofuel:biofuel",1} + basic_machines.grinder_recipes["farming:corn"] = {50,"biofuel:biofuel",1} + basic_machines.grinder_recipes["farming:potato"] = {50,"biofuel:biofuel",1} + basic_machines.grinder_recipes["default:papyrus"] = {70,"biofuel:biofuel",1} + end +end + +if core.get_modpath("default") then + core.register_craft({ + output = module_name .. ":biofuel_distiller", + recipe = { + {"default:copper_ingot", "default:copper_ingot", "default:copper_ingot"}, + {"default:steel_ingot" , "", "default:steel_ingot"}, + {"default:steel_ingot" , "default:steel_ingot", "default:steel_ingot"}, + }, + }) +end +if core.get_modpath("mcl_core") then + core.register_craft({ + output = module_name .. ":biofuel_distiller", + recipe = { + {"mcl_copper:copper_ingot", "mcl_copper:copper_ingot", "mcl_copper:copper_ingot"}, + {"mcl_core:iron_ingot" , "", "mcl_core:iron_ingot"}, + {"mcl_core:iron_ingot" , "mcl_core:iron_ingot", "mcl_core:iron_ingot"}, + }, + }) +end + + +-- biofuel +local new_gallon_id = "airutils:biofuel" +core.register_craftitem(new_gallon_id,{ + description = S("Bio Fuel"), + inventory_image = "airutils_biofuel_inv.png", +}) + +core.register_craft({ + type = "fuel", + recipe = new_gallon_id, + burntime = 50, +}) + +core.register_alias("biofuel:biofuel", new_gallon_id) --for the old biofuel + +local ferment = { + {"default:papyrus", new_gallon_id}, + {"farming:wheat", new_gallon_id}, + {"farming:corn", new_gallon_id}, + {"farming:baked_potato", new_gallon_id}, + {"farming:potato", new_gallon_id} +} + +local ferment_groups = {'flora', 'leaves', 'flower', 'sapling', 'tree', 'wood', 'stick', 'plant', 'seed', + 'leafdecay', 'leafdecay_drop', 'mushroom', 'vines' } + +-- distiller +local biofueldistiller_formspec = "size[8,9]" + .. "list[current_name;src;2,1;1,1;]" .. airutils.get_itemslot_bg(2, 1, 1, 1) + .. "list[current_name;dst;5,1;1,1;]" .. airutils.get_itemslot_bg(5, 1, 1, 1) + .. "list[current_player;main;0,5;8,4;]" .. airutils.get_itemslot_bg(0, 5, 8, 4) + .. "listring[current_name;dst]" + .. "listring[current_player;main]" + .. "listring[current_name;src]" + .. "listring[current_player;main]" + .. "image[3.5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]" + +core.register_node( module_name .. ":biofuel_distiller", { + description = S("Biofuel Distiller"), + tiles = {"airutils_black.png", "airutils_aluminum.png", "airutils_copper.png" }, + drawtype = "mesh", + mesh = "airutils_biofuel_distiller.b3d", + paramtype = "light", + paramtype2 = "facedir", + groups = { + choppy = 2, oddly_breakable_by_hand = 1, flammable = 2 + }, + legacy_facedir_simple = true, + + on_place = core.rotate_node, + + on_construct = function(pos) + + local meta = core.get_meta(pos) + + meta:set_string("formspec", biofueldistiller_formspec) + meta:set_string("infotext", S("Biofuel Distiller")) + meta:set_float("status", 0.0) + + local inv = meta:get_inventory() + + inv:set_size("src", 1) + inv:set_size("dst", 1) + end, + + can_dig = function(pos,player) + + local meta = core.get_meta(pos) + local inv = meta:get_inventory() + + if not inv:is_empty("dst") + or not inv:is_empty("src") then + return false + end + + return true + end, + + allow_metadata_inventory_take = function(pos, listname, index, stack, player) + + if core.is_protected(pos, player:get_player_name()) then + return 0 + end + + return stack:get_count() + end, + + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + + if core.is_protected(pos, player:get_player_name()) then + return 0 + end + + if listname == "src" then + return stack:get_count() + elseif listname == "dst" then + return 0 + end + end, + + allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + + if core.is_protected(pos, player:get_player_name()) then + return 0 + end + + if to_list == "src" then + return count + elseif to_list == "dst" then + return 0 + end + end, + + on_metadata_inventory_put = function(pos) + + local timer = core.get_node_timer(pos) + + timer:start(5) + end, + + on_timer = function(pos) + + local meta = core.get_meta(pos) ; if not meta then return end + local inv = meta:get_inventory() + + -- is barrel empty? + if not inv or inv:is_empty("src") then + + meta:set_float("status", 0.0) + meta:set_string("infotext", S("Fuel Distiller")) + + return false + end + + -- does it contain any of the source items on the list? + local has_item + + --normal items + for n = 1, #ferment do + if inv:contains_item("src", ItemStack(ferment[n][1])) then + has_item = n + break + end + end + + --groups + local has_group + if not has_item then + local inv_content = inv:get_list("src") + if inv_content then + for k, v in pairs(inv_content) do + local item_name = v:get_name() + for n = 1, #ferment_groups do + if core.get_item_group(item_name, ferment_groups[n]) == 1 then + has_group = n + break + end + end + end + end + end + + if not has_item and not has_group then + return false + end + + -- is there room for additional fermentation? + if has_item and not inv:room_for_item("dst", ferment[has_item][2]) then + meta:set_string("infotext", S("Fuel Distiller (FULL)")) + return true + end + + if has_group and not inv:room_for_item("dst", new_gallon_id) then + meta:set_string("infotext", S("Fuel Distiller (FULL)")) + return true + end + + local status = meta:get_float("status") + + -- fermenting (change status) + if status < 100 then + meta:set_string("infotext", S("Fuel Distiller @1% done", status)) + meta:set_float("status", status + 5) + else + if not has_group then + inv:remove_item("src", ferment[has_item][1]) + inv:add_item("dst", ferment[has_item][2]) + else + for i,itemstack in pairs(inv:get_list("src")) do + inv:remove_item("src", ItemStack(itemstack:get_name().." 1")) + end + inv:add_item("dst", new_gallon_id) + end + + meta:set_float("status", 0,0) + end + + if inv:is_empty("src") then + meta:set_float("status", 0.0) + meta:set_string("infotext", S("Fuel Distiller")) + end + + return true + end, +}) + +--lets remove the old one +core.register_node(":".."biofuel:biofuel_distiller", { + groups = {old_biofuel=1}, +}) + +core.register_abm({ + nodenames = {"group:old_biofuel"}, + interval = 1, + chance = 1, + action = function(pos, node) + --core.remove_node(pos) + core.swap_node(pos,{name = module_name..":biofuel_distiller"}) + end, +}) diff --git a/mods/airutils/airutils_papi.lua b/mods/airutils/airutils_papi.lua new file mode 100644 index 00000000..c59742af --- /dev/null +++ b/mods/airutils/airutils_papi.lua @@ -0,0 +1,170 @@ +local S = airutils.S + +local function check_protection(pos, name) + if core.is_protected(pos, name) then + core.log("action", name + .. " tried to place a PAPI" + .. " at protected position " + .. core.pos_to_string(pos) + ) + core.record_protection_violation(pos, name) + return true + end + return false +end + +function airutils.PAPIplace(player,pos) + if not player then + return + end + + local dir = core.dir_to_facedir(player:get_look_dir()) + + local player_name = player:get_player_name() + if check_protection(pos, player_name) then + return + end + + core.set_node(pos, {name="airutils:papi", param2=dir}) + local meta = core.get_meta(pos) + meta:set_string("infotext", S("PAPI") .. "\r" .. S("Owned by: @1", player_name)) + meta:set_string("owner", player_name) + meta:set_string("dont_destroy", "false") + return true +end + +function airutils.togglePapiSide(pos, node, clicker, itemstack) + local player_name = clicker:get_player_name() + local meta = core.get_meta(pos) + + if player_name ~= meta:get_string("owner") then + return + end + + local dir=node.param2 + if node.name == "airutils:papi_right" then + core.set_node(pos, {name="airutils:papi", param2=dir}) + meta:set_string("infotext", S("PAPI") .. " - " .. S("left side") .. "\r" .. S("Owned by: @1",player_name)) + elseif node.name == "airutils:papi" then + core.set_node(pos, {name="airutils:papi_right", param2=dir}) + meta:set_string("infotext", S("PAPI") .. " - " .. S("right side") .. "\r" .. S("Owned by: @1",player_name)) + end + + meta:set_string("owner", player_name) + meta:set_string("dont_destroy", "false") +end + +airutils.papi_collision_box = { + type = "fixed", + fixed={{-0.5,-0.5,-0.5,0.5,-0.42,0.5},}, +} + +airutils.papi_selection_box = { + type = "fixed", + fixed={{-0.5,-0.5,-0.5,0.5,1.5,0.5},}, +} + +airutils.groups_right = {snappy=2,choppy=2,oddly_breakable_by_hand=2,not_in_creative_inventory=1} +airutils.groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2} + +-- PAPI node (default left) +core.register_node("airutils:papi",{ + description = S("PAPI"), + --inventory_image = "papi.png", + --wield_image = "papi.png", + tiles = {"airutils_black.png", "airutils_u_black.png", "airutils_white.png", + "airutils_metal.png", {name = "airutils_red.png", backface_culling = true},}, + groups = airutils.groups, + paramtype2 = "facedir", + paramtype = "light", + drawtype = "mesh", + mesh = "papi.b3d", + visual_scale = 1.0, + light_source = 13, + backface_culling = true, + selection_box = airutils.papi_selection_box, + collision_box = airutils.papi_collision_box, + can_dig = airutils.canDig, + _color = "", + on_destruct = airutils.remove, + on_place = function(itemstack, placer, pointed_thing) + local pos = pointed_thing.above + if airutils.PAPIplace(placer,pos)==true then + itemstack:take_item(1) + return itemstack + else + return + end + end, + on_rightclick=airutils.togglePapiSide, + on_punch = function(pos, node, puncher, pointed_thing) + local player_name = puncher:get_player_name() + local meta = core.get_meta(pos) + if player_name ~= meta:get_string("owner") then + local privs = core.get_player_privs(player_name) + if privs.server == false then + return + end + end + end, +}) + +function airutils.remove_papi(pos) + --[[ + local meta = core.get_meta(pos) + local node = core.get_node(pos) + if node and meta then + local dir=node.param2 + if node.name == "airutils:papi_right" then + core.set_node(pos, {name="airutils:papi", param2=dir}) + meta:set_string("infotext", "PAPI - left side\rOwned by: "..player_name) + end + + meta:set_string("owner", player_name) + meta:set_string("dont_destroy", "false") + + if meta:get_string("dont_destroy") == "true" then + -- when swapping it + return + end + end]]-- +end + +-- PAPI right node +core.register_node("airutils:papi_right",{ + description = S("PAPI") .. "_" .. S("right_side"), + tiles = {"airutils_black.png", "airutils_u_black.png", "airutils_white.png", + "airutils_metal.png", {name = "airutils_red.png", backface_culling = true},}, + groups = airutils.groups_right, + paramtype2 = "facedir", + paramtype = "light", + drawtype = "mesh", + mesh = "papi_right.b3d", + visual_scale = 1.0, + light_source = 13, + backface_culling = true, + selection_box = airutils.papi_selection_box, + collision_box = airutils.papi_collision_box, + can_dig = airutils.canDig, + _color = "", + on_destruct = airutils.remove_papi, + on_rightclick=airutils.togglePapiSide, + on_punch = function(pos, node, puncher, pointed_thing) + local player_name = puncher:get_player_name() + local meta = core.get_meta(pos) + if player_name ~= meta:get_string("owner") then + return + end + end, +}) + + +-- PAPI craft +core.register_craft({ + output = 'airutils:papi', + recipe = { + {'default:glass', 'default:mese_crystal', 'default:glass'}, + {'default:glass', 'default:steel_ingot' , 'default:glass'}, + {'' , 'default:steel_ingot' , ''}, + } +}) diff --git a/mods/airutils/airutils_repair.lua b/mods/airutils/airutils_repair.lua new file mode 100644 index 00000000..c5a6885c --- /dev/null +++ b/mods/airutils/airutils_repair.lua @@ -0,0 +1,16 @@ +local S = airutils.S + +-- trike repair +core.register_craftitem("airutils:repair_tool",{ + description = S("Repair Tool"), + inventory_image = "airutils_repair_tool.png", +}) + +core.register_craft({ + output = "airutils:repair_tool", + recipe = { + {"", "default:steel_ingot", ""}, + {"", "default:steel_ingot", ""}, + {"default:steel_ingot", "", "default:steel_ingot"}, + }, +}) diff --git a/mods/airutils/airutils_tug.lua b/mods/airutils/airutils_tug.lua new file mode 100644 index 00000000..4b3dfb44 --- /dev/null +++ b/mods/airutils/airutils_tug.lua @@ -0,0 +1,105 @@ +local S = airutils.S + +function airutils.move_target(player, pointed_thing) + local pos = player:get_pos() + local yaw = player:get_look_horizontal() + + local object = pointed_thing.ref + --core.chat_send_all(dump(object)) + if object then + local obj_pos = object:get_pos() + if not obj_pos then return end + local hip = math.sqrt(math.pow(obj_pos.x - pos.x,2)+math.pow(obj_pos.z - pos.z,2)) + 1 + local pos_x = math.sin(yaw) * -hip + local pos_z = math.cos(yaw) * hip + obj_pos.x = pos.x + pos_x + obj_pos.z = pos.z + pos_z + + local node = core.get_node(obj_pos).name + local nodedef = core.registered_nodes[node] + local is_airlike = nodedef.drawtype == "airlike" + local is_liquid = (nodedef.drawtype == "flowingliquid" or nodedef.drawtype == "liquid") + + if player:get_player_control().sneak == true then + local rotation = object:get_rotation() + if rotation then + rotation.y = yaw + math.rad(180) + object:set_rotation(rotation) + end + else + if is_airlike or is_liquid then object:set_pos(obj_pos) end + end + --[[if object:get_attach() then + local dir = player:get_look_dir() + core.chat_send_all('detach') + object:set_detach() + object:set_rotation(dir) + else + core.chat_send_all('object found') + object:set_attach(player, "", {x=0, y=0, z=20}) + end]]-- + end +end + +core.register_tool("airutils:tug", { + description = S("Tug tool for airport"), + inventory_image = "airutils_tug.png", + stack_max=1, + on_use = function(itemstack, player, pointed_thing) + if not player then + return + end + + local is_admin = core.check_player_privs(player, {server=true}) + + local pos = player:get_pos() + local pname = player:get_player_name() + + --[[if areas then + if not areas:canInteract(pos, pname) then + local owners = areas:getNodeOwners(pos) + core.chat_send_player(pname, + S("@1 is protected by @2.", + core.pos_to_string(pos), + table.concat(owners, ", "))) + else + airutils.move_target(player, pointed_thing) + end + end]]-- + local is_protected = core.is_protected + if is_protected then + local owner = nil + local object = pointed_thing.ref + if object then + local ent = object:get_luaentity() + if ent then + if ent.owner then owner = ent.owner end + end + end + if not is_protected(pos, pname) or pname == owner or is_admin then + airutils.move_target(player, pointed_thing) + else + core.chat_send_player(pname, + S("@1 is protected.", + core.pos_to_string(pos))) + end + end + + if not is_protected then + airutils.move_target(player, pointed_thing) + end + + + end, + + sound = {breaks = "default_tool_breaks"}, +}) + +core.register_craft({ + output = "airutils:tug", + recipe = { + {"", "", "default:steel_ingot"}, + {"", "default:steel_ingot", ""}, + {"default:steel_ingot", "default:stick", "default:diamond"}, + } +}) diff --git a/mods/airutils/airutils_wind.lua b/mods/airutils/airutils_wind.lua new file mode 100644 index 00000000..47697990 --- /dev/null +++ b/mods/airutils/airutils_wind.lua @@ -0,0 +1,193 @@ +local S = airutils.S + +local function check_protection(pos, name) + if core.is_protected(pos, name) then + core.log("action", name + .. " tried to place a Wind Indicator" + .. " at protected position " + .. core.pos_to_string(pos) + ) + core.record_protection_violation(pos, name) + return true + end + return false +end + +function airutils.WindDplace(player,pos) + if not player then + return + end + + local dir = core.dir_to_facedir(vector.new()) + local pos1 = vector.new(pos) + + local player_name = player:get_player_name() + if check_protection(pos, player_name) then + return + end + + core.set_node(pos1, {name="airutils:wind", param2=dir}) + local meta = core.get_meta(pos) + meta:set_string("infotext", S("Wind Indicator") .. "\r" .. S("Owned by: @1",player_name)) + meta:set_string("owner", player_name) + meta:set_string("dont_destroy", "false") + return true +end + +airutils.wind_collision_box = { + type = "fixed", + fixed={{-0.5,0,-0.5,0.5,5.0,0.5},}, +} + +airutils.wind_selection_box = { + type = "fixed", + fixed={{-0.5,0,-0.5,0.5,5.0,0.5},}, +} + +local function get_smooth(angle_initial, reference, last_ref, value) + local range = reference-last_ref + local retVal = (value*angle_initial)/range + local retval = angle_initial - retVal + if retval < 0 then retval = 0 end + return retval +end + +core.register_entity("airutils:wind_indicator",{ + -- common props + physical = true, + stepheight = 0.5, + collide_with_objects = true, + collisionbox = {-0.5, 0, -0.5, 0.5, 5.0, 0.5}, + visual = "mesh", + mesh = "airutils_wind.b3d", + textures = {"airutils_red.png", "airutils_black.png", "airutils_white.png", "airutils_metal.png"}, + static_save = true, + makes_footstep_sound = false, + _pos = nil, + + on_activate = function(self, staticdata, dtime_s) + self._pos = self.object:get_pos() + end, + + on_step = function(self,dtime,colinfo) + self.object:set_pos(self._pos) + + local wind = airutils.get_wind(self._pos, 1.0) + local wind_yaw = core.dir_to_yaw(wind) + self.object:set_bone_position("ajuste", {x=0,y=42,z=0}, {x=0,y=0,z=90}) + self.object:set_bone_position("b_a", {x=0,y=0,z=0}, {x=math.deg(wind_yaw)-90,y=0,z=0}) + + local false_div = 1 --trying to make it more o minus sensible + local vel = ((vector.dot(vector.multiply(wind,dtime),wind))/false_div)*100 + --core.chat_send_all(vel) + local b_b = 65 + if vel > 11 then + b_b = get_smooth(65, 11, 0, vel) + end + self.object:set_bone_position("b_b", {x=0,y=8.25,z=0}, {x=0,y=0,z=-b_b}) + + local b_c = 15 + if vel > 16 then + b_c = get_smooth(15, 16, 11, vel) + end + self.object:set_bone_position("b_c", {x=0,y=6.0,z=0}, {x=0,y=0,z=-b_c}) + + local b_d = 5 + if vel > 22 then + b_d = get_smooth(5, 22, 16, vel) + end + self.object:set_bone_position("b_d", {x=0,y=4.5,z=0}, {x=0,y=0,z=-b_d}) + + local b_e = 2 + if vel > 28 then + b_e = get_smooth(2, 28, 22, vel) + end + self.object:set_bone_position("b_e", {x=0,y=3,z=0}, {x=0,y=0,z=-b_e}) + + --core.chat_send_all("Wind Direction: "..math.deg(wind_yaw)) + end, -- required + --on_activate = mobkit.actfunc, -- required + --get_staticdata = mobkit.statfunc, + max_hp = 65535, + timeout = 0, + on_punch=function(self, puncher) + return + end, + + on_rightclick = function(self, clicker) + local wind = airutils.get_wind(self.object:get_pos(), 2.0) + local wind_yaw = core.dir_to_yaw(wind) + core.chat_send_player(clicker:get_player_name(),core.colorize('#00ff00', S(" >>> The wind direction now is @1", math.deg(wind_yaw)))) + return + end, +}) + + + +-- Wind Indicator node (default left) +core.register_node("airutils:wind",{ + description = S("Wind Direction Indicator"), + waving = 1, + tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png"}, + paramtype = "light", + paramtype2 = "leveled", + is_ground_content = false, + groups = {cracky = 1, level = 2}, + walkable = true, + selection_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + {-0.1, 0.5, -0.1, 0.1, 2.0, 0.1} + } + }, + + node_dig_prediction = "default:dirt", + node_placement_prediction = "airutils:wind", + + on_place = function(itemstack, placer, pointed_thing) + + local pos = pointed_thing.above + + local player_name = placer:get_player_name() + + + if not core.is_protected(pos, player_name) and not core.is_protected(pos, player_name) then + core.set_node(pos, {name = "airutils:wind",param2 = 1 }) + core.add_entity({x=pos.x, y=pos.y, z=pos.z},"airutils:wind_indicator") + if not (creative and creative.is_enabled_for and creative.is_enabled_for(player_name)) then + itemstack:take_item() + end + else + core.chat_send_player(player_name, S("Node is protected")) + core.record_protection_violation(pos, player_name) + end + + + return itemstack + end, + + on_destruct = function(pos) + local meta=core.get_meta(pos) + if meta then + local cpos = {x=pos.x, y= pos.y, z=pos.z} + local object = core.get_objects_inside_radius(cpos, 1) + for _,obj in ipairs(object) do + local entity = obj:get_luaentity() + if entity and entity.name == "airutils:wind_indicator" then + obj:remove() + end + end + end + end, +}) + +-- WIND craft +core.register_craft({ + output = 'airutils:wind', + recipe = { + {'wool:white', 'wool:white', 'wool:white'}, + {'wool:white', 'default:steel_ingot' , 'wool:white'}, + {'' , 'default:steel_ingot' , ''}, + } +}) diff --git a/mods/airutils/attach_extern_ent.lua b/mods/airutils/attach_extern_ent.lua new file mode 100644 index 00000000..c29d657d --- /dev/null +++ b/mods/airutils/attach_extern_ent.lua @@ -0,0 +1,197 @@ +local S = airutils.S + +local function attach_entity(self, target_obj, ent, dest_pos, relative_pos, attach_up) + attach_up = attach_up or false + if not target_obj then return end + if self.object then + if self._vehicle_custom_data then + local y = 0 + if attach_up == false then + y = self.initial_properties.collisionbox[2] - ent.initial_properties.collisionbox[2] + else + y = (relative_pos.y/10) + ent.initial_properties.collisionbox[5] + end + dest_pos.y = dest_pos.y + y + target_obj:set_pos(dest_pos) + local ent = target_obj:get_luaentity() + if ent then + relative_pos.y = y*10 + target_obj:set_attach(self.object,'',relative_pos,{x=0,y=0,z=0}) + self._vehicle_custom_data.simple_external_attach_entity = ent.name + self._vehicle_custom_data.simple_external_attach_pos = relative_pos + self._vehicle_custom_data.simple_external_attach_invid = ent._inv_id --why?! Because I can identify the target entity by it's inventory ;) + end + end + end +end + +function airutils.get_attached_entity(self) + if not self._vehicle_custom_data then return nil, nil end + if not self._vehicle_custom_data.simple_external_attach_entity then return nil, nil end + + local inv_id = self._vehicle_custom_data.simple_external_attach_invid + + local pos = self.object:get_pos() + local nearby_objects = minetest.get_objects_inside_radius(pos, 32) + for i,obj in ipairs(nearby_objects) do + local ent = obj:get_luaentity() + if ent then + if ent._inv_id then + if ent._inv_id == inv_id then + return ent, obj + end + end + end + end + return nil, nil +end + +function airutils.dettach_entity(self) + local ent, obj = airutils.get_attached_entity(self) + if ent and obj then + local relative_pos = self._vehicle_custom_data.simple_external_attach_pos + local pos = self.object:get_pos() + local rotation = self.object:get_rotation() + local direction = rotation.y + local velocity = self.object:get_velocity() + + local move = -1*relative_pos.z/10 + pos.x = pos.x + move * math.sin(direction) + pos.z = pos.z - move * math.cos(direction) + pos.y = pos.y + self.initial_properties.collisionbox[2] - ent.initial_properties.collisionbox[2] + obj:set_detach() + obj:set_pos(pos) + obj:set_rotation(rotation) + obj:set_velocity(velocity) + --clear + self._vehicle_custom_data.simple_external_attach_entity = nil + self._vehicle_custom_data.simple_external_attach_pos = nil + self._vehicle_custom_data.simple_external_attach_invid = nil + end +end + +function airutils.attach_external_object(self, object, ent, destination_pos, relative_pos, attach_up) + local dest_pos = vector.new(destination_pos) + local rel_pos = vector.new(relative_pos) + if attach_up == false then + rel_pos.y = 0 + end + dest_pos = vector.add(dest_pos, vector.divide(rel_pos,10)) + attach_entity(self, object, ent, dest_pos, rel_pos, attach_up) +end + +function airutils.simple_external_attach(self, relative_pos, entity_name, radius, attach_up) + attach_up = attach_up or false + radius = radius or 12 + if self.object then + local curr_ent, _ = airutils.get_attached_entity(self) + if curr_ent then return end + + local pos = self.object:get_pos() + local nearby_objects = minetest.get_objects_inside_radius(pos, radius) + for i,obj in ipairs(nearby_objects) do + if obj == self.object then + table.remove(nearby_objects,i) + end + local ent = obj:get_luaentity() + if ent then + if ent.name == entity_name then + airutils.attach_external_object(self, nearby_objects[i], ent, pos, relative_pos, attach_up) + return + end + end + end + end +end + +--execute on load +function airutils.restore_external_attach(self) + if not self._vehicle_custom_data then return end + if not self._vehicle_custom_data.simple_external_attach_invid then return end + + local pos = self.object:get_pos() + local dest_pos = vector.new(pos) + local relative_pos = self._vehicle_custom_data.simple_external_attach_pos + local inv_id = self._vehicle_custom_data.simple_external_attach_invid + dest_pos = vector.add(dest_pos, relative_pos) + + minetest.after(0.3, function() + local nearby_objects = minetest.get_objects_inside_radius(pos, 32) + local ent + for i,obj in ipairs(nearby_objects) do + ent = obj:get_luaentity() + if ent then + --minetest.chat_send_all(dump(ent.name)) + if ent._inv_id then + --minetest.chat_send_all(">> "..dump(ent._inv_id).." >> "..dump(inv_id)) + if ent._inv_id == inv_id and ent._inv_id ~= self._inv_id then + --minetest.chat_send_all("++ "..dump(ent._inv_id).." ++ "..dump(inv_id)) + local target_obj = nearby_objects[i] + target_obj:set_pos(dest_pos) + target_obj:set_attach(self.object,'',relative_pos,{x=0,y=0,z=0}) + --attach_entity(self, nearby_objects[i], dest_pos, relative_pos, entity_name, inv_id) + return + end + end + end + end + end) + + --clear + --self._vehicle_custom_data.simple_external_attach_entity = nil + --self._vehicle_custom_data.simple_external_attach_pos = nil + --self._vehicle_custom_data.simple_external_attach_invid = nil +end + +minetest.register_chatcommand("remove_hook", { + params = "", + description = S("Dettach current vehicle from another"), + privs = {interact=true}, + func = function(name, param) + local colorstring = core.colorize('#ff0000', S(" >>> you are not inside a plane")) + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.on_step == airutils.on_step then + local rem_obj = entity.object:get_attach() + if not rem_obj then + minetest.chat_send_player(name,core.colorize('#ff0000', S(" >>> no hook found"))) + return + end + local rem_ent = rem_obj:get_luaentity() + + local pos = rem_ent.object:get_pos() + local rotation = rem_ent.object:get_rotation() + local direction = rotation.y + local velocity = rem_ent.object:get_velocity() + + local move = 0 + if rem_ent._vehicle_custom_data.simple_external_attach_pos then + move = -1*rem_ent._vehicle_custom_data.simple_external_attach_pos.z/10 + end + pos.x = pos.x + move * math.sin(direction) + pos.z = pos.z + move * math.cos(direction) + pos.y = pos.y + rem_ent.initial_properties.collisionbox[2] - entity.initial_properties.collisionbox[2] + entity.object:set_detach() + entity.object:set_pos(pos) + entity.object:set_rotation(rotation) + entity.object:set_velocity(velocity) + --clear + rem_ent._vehicle_custom_data.simple_external_attach_entity = nil + rem_ent._vehicle_custom_data.simple_external_attach_pos = nil + rem_ent._vehicle_custom_data.simple_external_attach_invid = nil + else + minetest.chat_send_player(name,colorstring) + end + end + end + else + minetest.chat_send_player(name,colorstring) + end + end +}) diff --git a/mods/airutils/common_entities.lua b/mods/airutils/common_entities.lua new file mode 100644 index 00000000..0f6bb645 --- /dev/null +++ b/mods/airutils/common_entities.lua @@ -0,0 +1,24 @@ +-- +-- seat pivot +-- +minetest.register_entity('airutils:seat_base',{ +initial_properties = { + physical = false, + collide_with_objects=false, + pointable=false, + visual = "mesh", + mesh = "airutils_seat_base.b3d", + textures = {"airutils_alpha.png",}, + }, + + on_activate = function(self,std) + self.sdata = minetest.deserialize(std) or {} + if self.sdata.remove then self.object:remove() end + end, + + get_staticdata=function(self) + self.sdata.remove=true + return minetest.serialize(self.sdata) + end, + +}) diff --git a/mods/airutils/description.txt b/mods/airutils/description.txt new file mode 100644 index 00000000..5e33bbc4 --- /dev/null +++ b/mods/airutils/description.txt @@ -0,0 +1 @@ +Airport Utils for Minetest diff --git a/mods/airutils/init.lua b/mods/airutils/init.lua new file mode 100644 index 00000000..097d1f86 --- /dev/null +++ b/mods/airutils/init.lua @@ -0,0 +1,676 @@ +-- Minetest 5.4.1 : airutils +airutils = {} + +airutils.storage = core.get_mod_storage() + +local storage = airutils.storage + +airutils.colors ={ + black='#2b2b2b', + blue='#0063b0', + brown='#8c5922', + cyan='#07B6BC', + dark_green='#567a42', + dark_grey='#6d6d6d', + green='#4ee34c', + grey='#9f9f9f', + magenta='#ff0098', + orange='#ff8b0e', + pink='#ff62c6', + red='#dc1818', + violet='#a437ff', + white='#FFFFFF', + yellow='#ffe400', +} + +airutils.S = nil + +if(core.get_translator ~= nil) then + airutils.S = core.get_translator(core.get_current_modname()) + +else + airutils.S = function ( s ) return s end +end + +local S = airutils.S + +local load_blast_damage = storage:get_int("blast_damage") +airutils.blast_damage = true +-- 1 == true ---- 2 == false +if load_blast_damage == 2 then airutils.blast_damage = false end + +airutils.is_minetest = core.get_modpath("player_api") +airutils.is_mcl = core.get_modpath("mcl_player") +airutils.is_repixture = core.get_modpath("rp_player") + +airutils.fuel = {['biofuel:biofuel'] = 1,['biofuel:bottle_fuel'] = 1, + ['biofuel:phial_fuel'] = 0.25, ['biofuel:fuel_can'] = 10, + ['airutils:biofuel'] = 1,} + +airutils.protect_in_areas = core.settings:get_bool('airutils_protect_in_areas') +airutils.debug_log = core.settings:get_bool('airutils_debug_log') + +if not core.settings:get_bool('airutils_disable_papi') then + dofile(core.get_modpath("airutils") .. DIR_DELIM .. "airutils_papi.lua") +end +if not core.settings:get_bool('airutils_disable_tug') then + dofile(core.get_modpath("airutils") .. DIR_DELIM .. "airutils_tug.lua") +end +if not core.settings:get_bool('airutils_disable_repair') then + dofile(core.get_modpath("airutils") .. DIR_DELIM .. "airutils_repair.lua") +end + +airutils.splash_texture = "airutils_splash.png" +airutils.use_water_particles = false +if core.settings:get_bool('airutils_enable_water_particles', false) then + airutils.use_water_particles = true +end + +airutils._use_signs_api = true +if not core.get_modpath("signs_lib") then airutils._use_signs_api = false end +if core.settings:get_bool('airutils_disable_signs_api') then airutils._use_signs_api = false end + +airutils.get_wind = dofile(core.get_modpath("airutils") .. DIR_DELIM ..'/wind.lua') +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "uuid_manager.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "common_entities.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "airutils_wind.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "water_splash.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "inventory_management.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "light.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "physics_lib.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "init.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "lib_copter" .. DIR_DELIM .. "init.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "texture_management.lua") +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "attach_extern_ent.lua") +if airutils._use_signs_api then dofile(core.get_modpath("airutils") .. DIR_DELIM .. "text.lua") end + +local is_biofuel_installed = false +if biomass then + if biomass.convertible_groups then is_biofuel_installed = true end +end +local enable_internal_biofuel = core.settings:get_bool('airutils.force_enable_biofuel') +if not is_biofuel_installed or enable_internal_biofuel then + dofile(core.get_modpath("airutils") .. DIR_DELIM .. "airutils_biofuel.lua") +end + +if core.get_modpath("player_api") and not core.settings:get_bool('airutils.disable_uniforms') then + dofile(core.get_modpath("airutils") .. DIR_DELIM .. "pilot_skin_manager.lua") +end + +function airutils.remove(pos) + local meta = core.get_meta(pos) + if meta:get_string("dont_destroy") == "true" then + -- when swapping it + return + end +end + +function airutils.canDig(pos, player) + local meta = core.get_meta(pos) + return meta:get_string("dont_destroy") ~= "true" + and player:get_player_name() == meta:get_string("owner") +end + +function airutils.check_node_below(obj, how_low) + local pos_below = obj:get_pos() + if pos_below then + pos_below.y = pos_below.y - how_low + local node_below = core.get_node(pos_below).name + local nodedef = core.registered_nodes[node_below] + local touching_ground = not nodedef or -- unknown nodes are solid + nodedef.walkable or false + local liquid_below = not touching_ground and nodedef.liquidtype ~= "none" + return touching_ground, liquid_below + end + return nil, nil +end + +function airutils.check_is_under_water(obj) + local pos_up = obj:get_pos() + pos_up.y = pos_up.y + 0.1 + local node_up = core.get_node(pos_up).name + local nodedef = core.registered_nodes[node_up] + local liquid_up = nodedef.liquidtype ~= "none" + return liquid_up +end + +function airutils.setText(self, vehicle_name) + local properties = self.object:get_properties() + local formatted = "" + if type(self.hp_max) ~= "number" then self.hp_max = 0.1 end --strange error when hpmax is NaN + if self.hp_max then + formatted = S(" Current hp: ") .. string.format( + "%.2f", self.hp_max + ) + end + if properties then + properties.infotext = S("Nice @1 of @2.@3", vehicle_name, self.owner, formatted) + self.object:set_properties(properties) + end +end + +function airutils.transfer_control(self, status) + if not self._have_copilot then return end + if status == false then + self._command_is_given = false + if self.co_pilot then + core.chat_send_player(self.co_pilot, + core.colorize('#ff0000', S(" >>> The captain got the control."))) + end + if self.driver_name then + core.chat_send_player(self.driver_name, + core.colorize('#00ff00', S(" >>> The control is with you now."))) + end + else + self._command_is_given = true + if self.co_pilot then + core.chat_send_player(self.co_pilot, + core.colorize('#00ff00', S(" >>> The control is with you now."))) + end + if self.driver_name then core.chat_send_player(self.driver_name,S(" >>> The control was given.")) end + end +end + +--returns 0 for old, 1 for new +function airutils.detect_player_api(player) + local player_proterties = player:get_properties() + --local mesh = "character.b3d" + --if player_proterties.mesh == mesh then + if core.get_modpath("player_api") then + local models = player_api.registered_models + local character = models[player_proterties.mesh] + --core.chat_send_all(dump(character)); + if character then + if character.animations.sit.eye_height then + --core.chat_send_all(dump(character.animations.sit.eye_height)); + if character.animations.sit.eye_height == 0.8 then + --core.chat_send_all("new model"); + return 1 + else + --core.chat_send_all("new height"); + return 2 --strange bug with armor ands skins returning 1.47 + end + else + --core.chat_send_all("old model"); + return 0 + end + end + end + + return 0 +end + +local function get_nodedef_field(nodename, fieldname) + if not core.registered_nodes[nodename] then + return nil + end + return core.registered_nodes[nodename][fieldname] +end + +--for +function airutils.eval_vertical_interception(initial_pos, end_pos) + local ret_y = nil + local cast = core.raycast(initial_pos, end_pos, true, true) + local thing = cast:next() + while thing do + if thing.type == "node" then + local pos = thing.intersection_point + if pos then + local nodename = core.get_node(thing.under).name + local drawtype = get_nodedef_field(nodename, "drawtype") + if drawtype ~= "plantlike" then + ret_y = pos.y + break + end + end + end + thing = cast:next() + end + return ret_y +end + +--lift +local function pitchroll2pitchyaw(aoa,roll) + if roll == 0.0 then return aoa,0 end + -- assumed vector x=0,y=0,z=1 + local p1 = math.tan(aoa) + local y = math.cos(roll)*p1 + local x = math.sqrt(p1^2-y^2) + local pitch = math.atan(y) + local yaw=math.atan(x)*math.sign(roll) + return pitch,yaw +end + +local function lerp(a, b, c) + return a + (b - a) * c +end + +function airutils.quadBezier(t, p0, p1, p2) + local l1 = lerp(p0, p1, t) + local l2 = lerp(p1, p2, t) + local quad = lerp(l1, l2, t) + return quad +end + +function airutils.get_ground_effect_lift(self, curr_pos, lift, wingspan) + local half_wingspan = wingspan/2 + local lower_collision = self.initial_properties.collisionbox[2] + if not self._ground_effect_ammount_percent then self._ground_effect_ammount_percent = 0.5 end + local initial_pos = {x=curr_pos.x, y=curr_pos.y + lower_collision, z=curr_pos.z} --lets make my own table to avoid interferences + + if self._extra_lift == nil then self._extra_lift = 0 end + if self._last_ground_effect_eval == nil then self._last_ground_effect_eval = 0 end + + self._last_ground_effect_eval = self._last_ground_effect_eval + self.dtime --dtime cames from airutils + + local ground_distance = wingspan + if self._last_ground_effect_eval >= 0.25 then + self._last_ground_effect_eval = 0 + self._last_ground_distance = ground_distance + local ground_y = airutils.eval_vertical_interception(initial_pos, {x=initial_pos.x, y=initial_pos.y - half_wingspan, z=initial_pos.z}) + if ground_y then + ground_distance = initial_pos.y - ground_y + end + --core.chat_send_all(dump(ground_distance)) + + --smooth the curve + local distance_factor = ((ground_distance) * 1) / (wingspan) + local effect_factor = airutils.quadBezier(distance_factor, 0, wingspan, 0) + if effect_factor < 0 then effect_factor = 0 end + if effect_factor > 0 then + effect_factor = math.abs( half_wingspan - effect_factor ) + end + + local lift_factor = ((effect_factor) * 1) / (half_wingspan) --agora isso é um percentual + local max_extra_lift_percent = self._ground_effect_ammount_percent * lift --e aqui o maximo extra de sustentação + local extra_lift = max_extra_lift_percent * lift_factor + self._extra_lift = extra_lift + end + + return self._extra_lift --return the value stored +end + + +-- velocity: velocity table +-- accel: current acceleration +-- longit_speed: the vehicle speed +-- roll: roll angle +-- curr_pos: current position +-- lift: lift factor (very simplified) +-- max_height: the max ceilling for the airplane +-- wingspan: for ground effect calculation +function airutils.getLiftAccel(self, velocity, accel, longit_speed, roll, curr_pos, in_lift, max_height, wingspan) + if longit_speed == nil then longit_speed = 0 end + wingspan = wingspan or 10 + local lift = in_lift + if not airutils.ground_effect_is_disabled then + local ground_effect_extra_lift = airutils.get_ground_effect_lift(self, curr_pos, in_lift, wingspan) + lift = lift + ground_effect_extra_lift + end + + --lift calculations + ----------------------------------------------------------- + max_height = max_height or 20000 + local wing_config = 0 + if self._wing_configuration then wing_config = self._wing_configuration end --flaps! + + local retval = accel + local min_speed = 1; + if self._min_speed then min_speed = self._min_speed end + min_speed = min_speed / 2 + + local striped_velocity = vector.new(velocity) + local cut_velocity = (min_speed * 1)/longit_speed + striped_velocity.x = striped_velocity.x - (striped_velocity.x * cut_velocity) + striped_velocity.z = striped_velocity.z - (striped_velocity.z * cut_velocity) + + local angle_of_attack = math.rad(self._angle_of_attack + wing_config) + --local acc = 0.8 + local daoa = math.deg(angle_of_attack) + --core.chat_send_all(dump(daoa)) + + --to decrease the lift coefficient at hight altitudes + local curr_percent_height = (100 - ((curr_pos.y * 100) / max_height))/100 + + local rotation=self.object:get_rotation() + local vrot = airutils.dir_to_rot(velocity,rotation) + + local hpitch,hyaw = pitchroll2pitchyaw(angle_of_attack,roll) + + local hrot = {x=vrot.x+hpitch,y=vrot.y-hyaw,z=roll} + local hdir = airutils.rot_to_dir(hrot) --(hrot) + local cross = vector.cross(velocity,hdir) + local lift_dir = vector.normalize(vector.cross(cross,hdir)) + + local lift_coefficient = (0.24*math.abs(daoa)*(1/(0.025*daoa+3))^4*math.sign(daoa)) + local lift_val = math.abs((lift*(vector.length(striped_velocity)^2)*lift_coefficient)*curr_percent_height) + if lift_val < 1 then lift_val = 1 end -- hipotetical aerodinamic wing will have no "lift" for down + + if self._climb_speed then --for helicopters + if (velocity.y) > self._climb_speed then lift_val = math.abs(airutils.gravity) end + end + if self._lift_dead_zone then + if lift_val < (math.abs(airutils.gravity)+self._lift_dead_zone) and lift_val > (math.abs(airutils.gravity)-self._lift_dead_zone) then + lift_val = math.abs(airutils.gravity) + end + end + + if airutils.show_lift then + core.chat_send_player(airutils.show_lift,core.colorize('#ffff00', " >>> lift: "..lift_val)) + end + + local lift_acc = vector.multiply(lift_dir,lift_val) + --lift_acc=vector.add(vector.multiply(core.yaw_to_dir(rotation.y),acc),lift_acc) + + retval = vector.add(retval,lift_acc) + ----------------------------------------------------------- + -- end lift + + return retval +end + +function airutils.get_plane_pitch(y_velocity, longit_speed, min_speed, angle_of_attack) + local v_speed_factor = 0 + if longit_speed > 0 then v_speed_factor = (y_velocity * math.rad(2)) end --the pitch for climbing or descenting + local min_rotation_speed = min_speed/2 + local pitch_by_longit_speed = 0 + if longit_speed > min_rotation_speed then --just start the rotation after the rotation speed + local scale_pitch_graph = ((longit_speed-min_rotation_speed)*1)/min_rotation_speed --lets use the min rotation speed for reference to when we will start the control work + if scale_pitch_graph > 1 then scale_pitch_graph = 1 end --normalize to 100% + pitch_by_longit_speed = airutils.quadBezier(scale_pitch_graph, 0, angle_of_attack, angle_of_attack) --here the magic happens using a bezier curve + end + return math.rad(pitch_by_longit_speed) + v_speed_factor +end + +function airutils.adjust_attack_angle_by_speed(angle_of_attack, min_angle, max_angle, limit, longit_speed, ideal_step, dtime) + --coloca em nivel gradualmente + local factor = 0 + if angle_of_attack > max_angle then factor = -1 end + if angle_of_attack < min_angle then factor = 1 end + local correction = (limit*(longit_speed/5000)) * factor * (dtime/ideal_step) + --core.chat_send_all("angle: "..angle_of_attack.." - correction: "..correction) + local new_angle_of_attack = angle_of_attack + correction + + return new_angle_of_attack +end + +function airutils.elevator_auto_correction(self, longit_speed, dtime, max_speed, elevator_angle, elevator_limit, ideal_step, intensity) + intensity = intensity or 500 + if longit_speed <= 0 then return end + local factor = 1 + + if self._elevator_angle > 0 then factor = -1 end + local ref_speed = longit_speed + if ref_speed > max_speed then ref_speed = max_speed end + local speed_scale = (elevator_limit + (elevator_limit/10)) - ((elevator_limit*ref_speed)/max_speed) + local divisor = intensity + speed_scale = speed_scale / divisor + local correction = speed_scale * factor * (dtime/ideal_step) + + local before_correction = elevator_angle + local new_elevator_angle = elevator_angle + correction + + if math.sign(before_correction) ~= math.sign(new_elevator_angle) then + new_elevator_angle = 0 + end + return new_elevator_angle +end + +function airutils.set_paint(self, puncher, itmstck, texture_name) + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + if item_name == "automobiles_lib:painter" or item_name == "bike:painter" then + --painting with bike painter + local meta = itmstck:get_meta() + local colstr = meta:get_string("paint_color") + --core.chat_send_all(dump(colstr)) + airutils.paint(self, colstr, texture_name) + return true + else + --painting with dyes + local split = string.split(item_name, ":") + local indx, _ + if split[1] then _,indx = split[1]:find('dye') end + if indx then + --[[for clr,_ in pairs(airutils.colors) do + local _,x = split[2]:find(clr) + if x then color = clr end + end]]-- + --lets paint!!!! + local color = (item_name:sub(indx+1)):gsub(":", "") + local colstr = airutils.colors[color] + --core.chat_send_all(color ..' '.. dump(colstr)) + if colstr then + airutils.paint(self, colstr, texture_name) + if self._alternate_painting_texture and self._mask_painting_texture then + airutils.paint_with_mask(self, colstr, self._alternate_painting_texture, self._mask_painting_texture) + end + itmstck:set_count(itmstck:get_count()-1) + if puncher ~= nil then puncher:set_wielded_item(itmstck) end + return true + end + -- end painting + end + end + return false +end + +function airutils._set_name(self) + if not airutils._use_signs_api then return end + local l_textures = self.object:get_properties().textures --self.initial_properties.textures + for _, texture in ipairs(l_textures) do + local indx = texture:find('airutils_name_canvas.png') + if indx then + l_textures[_] = "airutils_name_canvas.png^"..airutils.convert_text_to_texture(self._ship_name, self._name_color or 0, self._name_hor_aligment or 0.8) + end + end + self.object:set_properties({textures=l_textures}) +end + +--painting +function airutils.paint(self, colstr, texture_name) + if not self then return end + if colstr then + self._color = colstr + local l_textures = self.initial_properties.textures + for _, texture in ipairs(l_textures) do + local indx = texture:find(texture_name) + if indx then + l_textures[_] = texture_name.."^[multiply:".. colstr + end + end + self.object:set_properties({textures=l_textures}) + end +end + +function airutils.getAngleFromPositions(origin, destiny) + local angle_north = math.deg(math.atan2(destiny.x - origin.x, destiny.z - origin.z)) + if angle_north < 0 then angle_north = angle_north + 360 end + return angle_north +end + +function airutils.sit(player) + --set_animation(frame_range, frame_speed, frame_blend, frame_loop) + player:set_animation({x = 81, y = 160},30, 0, true) + if core.get_modpath("emote") then emote.start(player:get_player_name(), "sit") end +end + +local function get_norm_angle(angle) + local new_angle = angle/360 + new_angle = (new_angle - math.floor(new_angle))*360 + if new_angle < -180 then new_angle = new_angle + 360 end + if new_angle > 180 then new_angle = new_angle - 360 end + return new_angle +end + +function airutils.normalize_rotations(rotations) + return {x = get_norm_angle(rotations.x), y = get_norm_angle(rotations.y), z = get_norm_angle(rotations.z)} +end + +core.register_chatcommand("enable_blast_damage", { + params = "", + description = S("Enable/disable explosion blast damage"), + privs = {server=true}, + func = function(name, param) + local command = param + + if command == "false" then + airutils.blast_damage = false + core.chat_send_player(name, S(">>> Blast damage by explosion is disabled")) + else + airutils.blast_damage = true + core.chat_send_player(name, S(">>> Blast damage by explosion is enabled")) + end + local save = 2 + if airutils.blast_damage == true then save = 1 end + storage:set_int("blast_damage", save) + end, +}) + +core.register_chatcommand("transfer_ownership", { + params = "", + description = S("Transfer the property of a plane to another player"), + privs = {interact=true}, + func = function(name, param) + local player = core.get_player_by_name(name) + local target_player = core.get_player_by_name(param) + local attached_to = player:get_attach() + + if attached_to ~= nil then + if target_player ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.owner == name or core.check_player_privs(name, {protection_bypass=true}) then + entity.owner = param + core.chat_send_player(name,core.colorize('#00ff00', S(" >>> This plane now is property of: ")..param)) + else + core.chat_send_player(name,core.colorize('#ff0000', S(" >>> only the owner or moderators can transfer this airplane"))) + end + end + end + else + core.chat_send_player(name,core.colorize('#ff0000', S(" >>> the target player must be logged in"))) + end + else + core.chat_send_player(name,core.colorize('#ff0000', S(" >>> you are not inside a plane to perform the command"))) + end + end +}) + +core.register_chatcommand("eject_from_plane", { + params = "", + description = S("Ejects from a plane"), + privs = {interact = true}, + func = function(name, param) + local colorstring = core.colorize('#ff0000', S(" >>> you are not inside a plane")) + local player = core.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.on_step == airutils.on_step then + if entity.driver_name == name then + airutils.dettachPlayer(entity, player) + elseif entity._passenger == name then + local passenger = core.get_player_by_name(entity._passenger) + airutils.dettach_pax(entity, passenger) + end + else + core.chat_send_player(name,colorstring) + end + end + end + else + core.chat_send_player(name,colorstring) + end + end +}) + +core.register_chatcommand("ground_effect", { + params = "", + description = S("Enables/disables the ground effect (for debug purposes)"), + privs = {server=true}, + func = function(name, param) + if core.check_player_privs(name, {server=true}) then + if param == "on" or param == "" then + airutils.ground_effect_is_disabled = nil + core.chat_send_player(name,core.colorize('#00ff00', S(" >>> Ground effect was turned on."))) + elseif param == "off" then + airutils.ground_effect_is_disabled = true + core.chat_send_player(name,core.colorize('#0000ff', S(">>> Ground effect was turned off."))) + end + else + core.chat_send_player(name,core.colorize('#ff0000', S(" >>> You need 'server' priv to run this command."))) + end + end +}) + +core.register_chatcommand("show_lift", { + params = "", + description = S("Enables/disables the lift printing (for debug purposes)"), + privs = {server=true}, + func = function(name, param) + if core.check_player_privs(name, {server=true}) then + if param == "on" or param == "" then + airutils.show_lift = name + core.chat_send_player(name,core.colorize('#0000ff', S(" >>> Lift printing turned on."))) + elseif param == "off" then + airutils.show_lift = nil + core.chat_send_player(name,core.colorize('#00ff00', S(" >>> Lift printing turned off."))) + end + else + core.chat_send_player(name,core.colorize('#ff0000', S(" >>> You need 'server' priv to run this command."))) + end + end +}) + +if airutils._use_signs_api then + local function prefix_change(name, param) + local colorstring = core.colorize('#ff0000', S(" >>> you are not inside a vehicle")) + local player = core.get_player_by_name(name) + if not player then return end + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.owner == name or core.check_player_privs(name, {protection_bypass=true}) then + if param then + entity._ship_name = string.sub(param, 1, 40) + else + entity._ship_name = "" + end + airutils._set_name(entity) + core.chat_send_player(name,core.colorize('#00ff00', S(" >>> the vehicle name was changed"))) + else + core.chat_send_player(name,core.colorize('#ff0000', S(" >>> only the owner or moderators can name this vehicle"))) + end + end + end + else + core.chat_send_player(name,colorstring) + end + end + + core.register_chatcommand("set_vehicle_name", { + params = "", + description = S("this command is an alias for /set_prefix"), + privs = {interact = true}, + func = prefix_change, + }) + + core.register_chatcommand("set_prefix", { + params = "", + description = S("Sets the vehicle prefix"), + privs = {interact = true}, + func = prefix_change, + }) +end diff --git a/mods/airutils/inventory_management.lua b/mods/airutils/inventory_management.lua new file mode 100644 index 00000000..9d70477d --- /dev/null +++ b/mods/airutils/inventory_management.lua @@ -0,0 +1,217 @@ +local storage = airutils.storage +airutils.modname = core.get_current_modname() + +--function to format formspec for mineclone. In case of minetest, just returns an empty string +function airutils.get_itemslot_bg(a, b, c, d) + if airutils.is_mcl then + if mcl_formspec then + return mcl_formspec.get_itemslot_bg(a,b,c,d) + end + end + return "" +end + +local function get_formspec_by_size(self, size) + local background = "" + local hotbar = "" + local is_minetest_game = airutils.is_minetest or false + if is_minetest_game then + background = background .. default.gui_bg .. default.gui_bg_img .. default.gui_slots + hotbar = default.get_hotbar_bg(0,4.85) + end + local default_inventory_formspecs = { + ["2"]="size[8,6]".. background .. + "list[detached:" .. self._inv_id .. ";main;3.0,0;3,1;]" .. airutils.get_itemslot_bg(3.0, 0, 2, 1) .. + "list[current_player;main;0,2;8,4;]" .. airutils.get_itemslot_bg(0, 2, 8, 4) .. + "listring[]", + + ["3"]="size[8,6]".. background .. + "list[detached:" .. self._inv_id .. ";main;2.5,0;3,1;]" .. airutils.get_itemslot_bg(2.5, 0, 3, 1) .. + "list[current_player;main;0,2;8,4;]" .. airutils.get_itemslot_bg(0, 2, 8, 4) .. + "listring[]", + + ["4"]="size[8,6]".. background .. + "list[detached:" .. self._inv_id .. ";main;2,0;4,1;]" .. airutils.get_itemslot_bg(2.0, 0, 4, 1) .. + "list[current_player;main;0,2;8,4;]" .. airutils.get_itemslot_bg(0, 2, 8, 4) .. + "listring[]", + + ["6"]="size[8,6]".. background .. + "list[detached:" .. self._inv_id .. ";main;1,0;6,1;]".. airutils.get_itemslot_bg(1.0, 0, 6, 1) .. + "list[current_player;main;0,2;8,4;]" .. airutils.get_itemslot_bg(0, 2, 8, 4) .. + "listring[]", + + ["8"]="size[8,6]".. background .. + "list[detached:" .. self._inv_id .. ";main;0,0;8,1;]".. airutils.get_itemslot_bg(0, 0, 8, 1) .. + "list[current_player;main;0,2;8,4;]" .. airutils.get_itemslot_bg(0, 2, 8, 4) .. + "listring[]", + + ["12"]="size[8,7]".. background .. + "list[detached:" .. self._inv_id .. ";main;1,0;6,2;]".. airutils.get_itemslot_bg(1, 0, 6, 2) .. + "list[current_player;main;0,3;8,4;]" .. airutils.get_itemslot_bg(0, 3, 8, 4) .. + "listring[]", + + ["16"]="size[8,7]".. background .. + "list[detached:" .. self._inv_id .. ";main;0,0;8,2;]".. airutils.get_itemslot_bg(0, 0, 8, 2) .. + "list[current_player;main;0,3;8,4;]" .. airutils.get_itemslot_bg(0, 3, 8, 4) .. + "listring[]", + + ["24"]="size[8,8]".. background .. + "list[detached:" .. self._inv_id .. ";main;0,0;8,3;]".. airutils.get_itemslot_bg(0, 0, 8, 3) .. + "list[current_player;main;0,4;8,4;]" .. airutils.get_itemslot_bg(0, 4, 8, 4) .. + "listring[]", + + ["32"]="size[8,9]".. background .. + "list[detached:" .. self._inv_id .. ";main;0,0.3;8,4;]".. airutils.get_itemslot_bg(0, 0.3, 8, 4) .. + "list[current_player;main;0,5;8,4;]".. airutils.get_itemslot_bg(0, 5, 8, 4) .. + "listring[]" .. + hotbar, + + ["50"]="size[10,10]".. background .. + "list[detached:" .. self._inv_id .. ";main;0,0;10,5;]".. airutils.get_itemslot_bg(0, 0, 10, 5) .. + "list[current_player;main;1,6;8,4;]" .. airutils.get_itemslot_bg(1, 6, 8, 4) .. + "listring[]", + } + + local formspec = default_inventory_formspecs[tostring(size)] + return formspec +end + +local function inventory_id(maker_name) + local id= airutils.modname .. "_" .. maker_name .. "_" + for i=0,5 do + id=id..(math.random(0,9)) + end + return id +end + +function airutils.load_inventory(self) + if self._inv then + local inv_content = core.deserialize(storage:get_string(self._inv_id)) + if inv_content then + self._inv:set_list("main", inv_content) + end + end +end + +function airutils.save_inventory(self) + if self._inv then + local inv_content = self._inv:get_list("main") + if inv_content then + for k, v in pairs(inv_content) do + inv_content[k] = v:to_string() + end + + local inv_content = core.serialize(inv_content) + storage:set_string(self._inv_id, inv_content) + end + end +end + +function airutils.remove_inventory(self) + local inventory = airutils.get_inventory(self) + if inventory then + if inventory:is_empty("main") then + return core.remove_detached_inventory(self._inv_id) + else + local inv_content = inventory:get_list("main") + if inv_content then + local pos = self.object:get_pos() + for k, v in pairs(inv_content) do + local count = 0 + for i = 0,v:get_count()-1,1 + do + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},v:get_name()) + count = count + 1 + if count >= 5 then break end + end + end + end + return core.remove_detached_inventory(self._inv_id) + end + end + return false +end + +function airutils.destroy_inventory(self) + airutils.remove_inventory(self) + storage:set_string(self._inv_id, nil) +end + +--show inventory form to user +function airutils.show_vehicle_trunk_formspec(self, player, size) + local form = get_formspec_by_size(self, size) + core.show_formspec(player:get_player_name(), airutils.modname .. ":inventory", + form + ) +end + +function airutils.create_inventory(self, size, owner) + owner = owner or "" + if owner == "" then owner = self.owner end + --core.chat_send_all("slots: " .. size) + if owner ~= nil and owner ~= "" then + if self._inv_id == "" then + self._inv_id = inventory_id(owner) + end + local vehicle_inv = core.create_detached_inventory(self._inv_id, { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + local is_moderator = core.check_player_privs(player, {server=true}) or core.check_player_privs(player, {protection_bypass=true}) + if player:get_player_name() ~= owner and is_moderator == false then + return 0 + end + return count -- allow moving + end, + + allow_put = function(inv, listname, index, stack, player) + local is_moderator = core.check_player_privs(player, {server=true}) or core.check_player_privs(player, {protection_bypass=true}) + if player:get_player_name() ~= owner and is_moderator == false then + return 0 + end + return stack:get_count() -- allow putting + end, + + allow_take = function(inv, listname, index, stack, player) + local is_moderator = core.check_player_privs(player, {server=true}) or core.check_player_privs(player, {protection_bypass=true}) + if player:get_player_name() ~= owner and is_moderator == false then + return 0 + end + return stack:get_count() -- allow taking + end, + on_put = function(inv, toList, toIndex, stack, player) + airutils.save_inventory(self) + end, + on_take = function(inv, toList, toIndex, stack, player) + airutils.save_inventory(self) + end, + on_move = function(inv, from_list, from_index, to_list, to_index, count, player) + airutils.save_inventory(self) + end, + }, owner) + if size >= 8 then + if vehicle_inv:set_size("main", size) then + vehicle_inv:set_width("main", 8) + end + else + vehicle_inv:set_size("main", size) + end + self._inv = vehicle_inv + airutils.load_inventory(self) + end +end + +function airutils.get_inventory(self) + if self._inv then + return core.get_inventory({type="detached", name=self._inv_id}) + end + return nil +end + +function airutils.list_inventory(self) + local inventory = airutils.get_inventory(self) + if inventory then + local list = inventory.get_list("main") + + core.chat_send_all(dump(list)) + end +end + diff --git a/mods/airutils/lib_copter/control.lua b/mods/airutils/lib_copter/control.lua new file mode 100644 index 00000000..da1eecbe --- /dev/null +++ b/mods/airutils/lib_copter/control.lua @@ -0,0 +1,167 @@ +--[[airutils.rudder_limit = 30 +airutils.elevator_limit = 40]]-- +local S = airutils.S + +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "utilities.lua") + +local function set_yaw_by_mouse(self, dir) + local rotation = self.object:get_rotation() + local rot_y = math.deg(rotation.y) + + local total = math.abs(math.floor(rot_y/360)) + + if rot_y < 0 then rot_y = rot_y + (360*total) end + if rot_y > 360 then rot_y = rot_y - (360*total) end + if rot_y >= 270 and dir <= 90 then dir = dir + 360 end + if rot_y <= 90 and dir >= 270 then dir = dir - 360 end + + local intensity = self._yaw_intensity / 10 + local command = (rot_y - dir) * intensity + if command < -90 then command = -90 + elseif command > 90 then command = 90 end + --minetest.chat_send_all("rotation y: "..rot_y.." - dir: "..dir.." - command: "..command) + + self._rudder_angle = (-command * self._rudder_limit)/90 +end + +local function set_yaw(self, dir, dtime) + local yaw_factor = self._yaw_intensity or 25 + if dir == 1 then + self._rudder_angle = math.max(self._rudder_angle-(yaw_factor*dtime),-self._rudder_limit) + elseif dir == -1 then + self._rudder_angle = math.min(self._rudder_angle+(yaw_factor*dtime),self._rudder_limit) + end +end + +function airutils.heli_control(self, dtime, hull_direction, longit_speed, longit_drag, nhdir, + later_speed, later_drag, accel, player, is_flying) + --if self.driver_name == nil then return end + local retval_accel = accel + + local stop = false + local ctrl + + local time_correction = (dtime/airutils.ideal_step) + if time_correction < 1 then time_correction = 1 end + self._vehicle_acc = self._vehicle_acc or 0 + + -- player control + if player then + ctrl = player:get_player_control() + + if self._last_time_command > 0.5 then + self._last_time_command = 0.5 + end + + if not self._acceleration then self._acceleration = 0 end + if not self._lat_acceleration then self._lat_acceleration = 0 end + + if self._engine_running then + --control lift + local collective_up_max = 1.2 + local min_angle = self._min_collective + local collective_up = collective_up_max / 10 + if ctrl.jump then + self._wing_configuration = self._wing_configuration + collective_up + --end + self._is_going_up = true + elseif ctrl.sneak then + self._wing_configuration = self._wing_configuration - collective_up + --end + else + self._wing_configuration = self._stable_collective + end + if self._wing_configuration < min_angle then self._wing_configuration = min_angle end + local up_limit = (self._wing_angle_of_attack+collective_up_max) + if self._wing_configuration > up_limit then self._wing_configuration = up_limit end + --end lift + else + self._wing_configuration = self._stable_collective or 1 + end + + local yaw_cmd = 0 + if is_flying or self.wheels then + local acc_fraction = (self._max_engine_acc / 40)*time_correction + if ctrl.up then + if longit_speed < self._max_speed then + self._acceleration = self._acceleration + acc_fraction + else + self._acceleration = 0 + end + elseif ctrl.down then + if longit_speed > -self._max_speed then + self._acceleration = self._acceleration + (-acc_fraction) + else + self._acceleration = 0 + end + else + self._acceleration = 0 + end + self._acceleration = math.min(self._acceleration,self._max_engine_acc) + self._acceleration = math.max(self._acceleration,-self._max_engine_acc) + + if is_flying then --why double check? because I dont want lateral movement when landed + if ctrl.right then + yaw_cmd = 1 + if later_speed < self._max_speed and self._yaw_by_mouse then + self._lat_acceleration = self._lat_acceleration + acc_fraction + else + self._lat_acceleration = 0 + end + elseif ctrl.left then + yaw_cmd = -1 + if later_speed > -self._max_speed and self._yaw_by_mouse then + self._lat_acceleration = self._lat_acceleration + (-acc_fraction) + else + self._lat_acceleration = 0 + end + else + self._lat_acceleration = 0 + end + end + else + self._acceleration = 0 + self._lat_acceleration = 0 + self.object:set_velocity({x=0,y=self.object:get_velocity().y,z=0}) + end + + self._vehicle_acc = math.min(self._acceleration, self._max_engine_acc) + self._vehicle_acc = math.max(self._acceleration,-self._max_engine_acc) + self._lat_acc = math.min(self._lat_acceleration, self._max_engine_acc) + + local hull_acc = vector.multiply(hull_direction,self._vehicle_acc) + local lat_hull_acc = vector.multiply(nhdir,self._lat_acc) + --colocar aceleração lateral aqui + retval_accel=vector.add(retval_accel,hull_acc) + retval_accel=vector.add(retval_accel,lat_hull_acc) + + -- yaw + if self._yaw_by_mouse then + local rot_y = math.deg(player:get_look_horizontal()) + set_yaw_by_mouse(self, rot_y) + else + set_yaw(self, yaw_cmd, dtime) + end + + --I'm desperate, center all! + if ctrl.right and ctrl.left then + self._wing_configuration = self._stable_collective + end + + if ctrl.up and ctrl.down and self._last_time_command >= 0.5 then + self._last_time_command = 0 + local name = player:get_player_name() + if self._yaw_by_mouse == true then + minetest.chat_send_player(name, core.colorize('#0000ff', S(" >>> Mouse control disabled."))) + self._yaw_by_mouse = false + else + minetest.chat_send_player(name, core.colorize('#0000ff', S(" >>> Mouse control enabled."))) + self._yaw_by_mouse = true + end + end + end + + return retval_accel, stop +end + + diff --git a/mods/airutils/lib_copter/entities.lua b/mods/airutils/lib_copter/entities.lua new file mode 100644 index 00000000..c7baa4ed --- /dev/null +++ b/mods/airutils/lib_copter/entities.lua @@ -0,0 +1,382 @@ +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") + +local function engineSoundPlay(self, increment, base) + increment = increment or 0.0 + --sound + if self.sound_handle then minetest.sound_stop(self.sound_handle) end + if self.object then + local base_pitch = base + local pitch_adjust = base_pitch + increment + self.sound_handle = minetest.sound_play({name = self._engine_sound}, + {object = self.object, gain = 2.0, + pitch = pitch_adjust, + max_hear_distance = 32, + loop = true,}) + end +end + +local function engine_set_sound_and_animation(self, is_flying, newpitch, newroll) + is_flying = is_flying or false + + if self._engine_running then --engine running + if not self.sound_handle then + engineSoundPlay(self, 0.0, 0.9) + end + --self._cmd_snd + if self._snd_last_cmd ~= self._cmd_snd then + local increment + self._snd_last_cmd = self._cmd_snd + if self._cmd_snd then increment = 0.1 else increment = 0.0 end + engineSoundPlay(self, increment, 0.9) + end + + self.object:set_animation_frame_speed(100) + else + if is_flying then --autorotation here + if self._snd_last_cmd ~= self._cmd_snd then + local increment + self._snd_last_cmd = self._cmd_snd + if self._cmd_snd then increment = 0.1 else increment = 0.0 end + engineSoundPlay(self, increment, 0.6) + end + + self.object:set_animation_frame_speed(70) + else --stop all + if self.sound_handle then + self._snd_last_roll = nil + self._snd_last_pitch = nil + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + self.object:set_animation_frame_speed(0) + end + end + end +end + +function airutils.logic_heli(self) + local velocity = self.object:get_velocity() + local curr_pos = self.object:get_pos() + self._curr_pos = curr_pos --shared + self._last_accel = self.object:get_acceleration() + + self._last_time_command = self._last_time_command + self.dtime + + if self._last_time_command > 1 then self._last_time_command = 1 end + + local player = nil + if self.driver_name then player = minetest.get_player_by_name(self.driver_name) end + local co_pilot = nil + if self.co_pilot and self._have_copilot then co_pilot = minetest.get_player_by_name(self.co_pilot) end + + --test collision + airutils.testImpact(self, velocity, curr_pos) + + local ctrl = nil + if player then + ctrl = player:get_player_control() + --------------------- + -- change the driver + --------------------- + if co_pilot and self._have_copilot and self._last_time_command >= 1 then + if self._command_is_given == true then + if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then + self._last_time_command = 0 + --take the control + airutils.transfer_control(self, false) + end + else + if ctrl.sneak == true and ctrl.jump == true then + self._last_time_command = 0 + --trasnfer the control to student + airutils.transfer_control(self, true) + end + end + end + end + + + if not self.object:get_acceleration() then return end + local accel_y = self.object:get_acceleration().y + local rotation = self.object:get_rotation() + local yaw = rotation.y + local newyaw=yaw + local roll = rotation.z + + local hull_direction = airutils.rot_to_dir(rotation) --minetest.yaw_to_dir(yaw) + local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector + + local longit_speed = vector.dot(velocity,hull_direction) + self._longit_speed = longit_speed + + local longit_drag = vector.multiply(hull_direction,longit_speed* + longit_speed*self._longit_drag_factor*-1*airutils.sign(longit_speed)) + local later_speed = airutils.dot(velocity,nhdir) + --minetest.chat_send_all('later_speed: '.. later_speed) + local later_drag = vector.multiply(nhdir,later_speed*later_speed* + self._later_drag_factor*-1*airutils.sign(later_speed)) + local accel = vector.add(longit_drag,later_drag) + local stop = false + + local is_flying = true + if self.colinfo then + is_flying = (not self.colinfo.touching_ground) + end + if self.isinliquid == true then + is_flying = false + end + --if self.isonground then is_flying = false end + --if is_flying then minetest.chat_send_all('is flying') end + + local is_attached = airutils.checkAttach(self, player) + if self._indicated_speed == nil then self._indicated_speed = 0 end + + if not is_attached then + -- for some engine error the player can be detached from the machine, so lets set him attached again + airutils.checkattachBug(self) + end + + if self._custom_step_additional_function then + self._custom_step_additional_function(self) + end + + --landing light + if self._have_landing_lights then + airutils.landing_lights_operate(self) + end + + --smoke and fire + if self._engine_running then + local curr_health_percent = (self.hp_max * 100)/self._max_plane_hp + if curr_health_percent < 20 then + airutils.add_smoke_trail(self, 2) + elseif curr_health_percent < 50 then + airutils.add_smoke_trail(self, 1) + end + else + if self._smoke_spawner and not self._smoke_semaphore then + self._smoke_semaphore = 1 --to set it only one time + minetest.after(5, function() + if self._smoke_spawner then + minetest.delete_particlespawner(self._smoke_spawner) + self._smoke_spawner = nil + self._smoke_semaphore = nil + end + end) + end + end + + if (math.abs(velocity.x) < 0.1 and math.abs(velocity.z) < 0.1) and is_flying == false and is_attached == false and self._engine_running == false then + if self._ground_friction then + if not self.isinliquid then self.object:set_velocity({x=0,y=airutils.gravity*self.dtime,z=0}) end + end + engine_set_sound_and_animation(self, false, 0, 0) + return + end + + --adjust climb indicator + local y_velocity = 0 + if self._engine_running or is_flying then y_velocity = velocity.y end + local climb_rate = y_velocity + if climb_rate > 5 then climb_rate = 5 end + if climb_rate < -5 then + climb_rate = -5 + end + + -- pitch and roll + local newroll = 0 + local newpitch = 0 + if ctrl and is_flying then + local command_angle = self._tilt_angle or 0 + local max_acc = self._max_engine_acc + + local pitch_ammount = (math.abs(self._vehicle_acc or 0) * command_angle) / max_acc + if math.abs(longit_speed) >= self._max_speed then pitch_ammount = command_angle end --gambiarra pra continuar com angulo + pitch_ammount = math.rad(math.min(pitch_ammount, command_angle)) + + if ctrl.up then newpitch = -pitch_ammount end + if ctrl.down then newpitch = pitch_ammount end + + local roll_ammount = (math.abs(self._lat_acc or 0) * command_angle) / max_acc + if math.abs(later_speed) >= self._max_speed then roll_ammount = command_angle end --gambiarra pra continuar com angulo + roll_ammount = math.rad(math.min(roll_ammount, command_angle)) + + if ctrl.left then newroll = -roll_ammount end + if ctrl.right then newroll = roll_ammount end + + if ctrl.up or ctrl.down or ctrl.left or ctrl.right then + self._cmd_snd = true + else + self._cmd_snd = false + end + end + + -- new yaw + if math.abs(self._rudder_angle)>1.5 then + local turn_rate = math.rad(self._yaw_turn_rate) + local yaw_turn = self.dtime * math.rad(self._rudder_angle) * turn_rate * 4 + newyaw = yaw + yaw_turn + end + + --------------------------------- + -- end roll + + local pilot = player + if self._have_copilot then + if self._command_is_given and co_pilot then + pilot = co_pilot + else + self._command_is_given = false + end + end + + ------------------------------------------------------ + --accell calculation block + ------------------------------------------------------ + if is_attached or co_pilot then + accel, stop = airutils.heli_control(self, self.dtime, hull_direction, + longit_speed, longit_drag, nhdir, later_speed, later_drag, accel, pilot, is_flying) + end + --end accell + + --get disconnected players + airutils.rescueConnectionFailedPassengers(self) + + if accel == nil then accel = {x=0,y=0,z=0} end + + --lift calculation + accel.y = accel_y + + --lets apply some bob in water + if self.isinliquid then + local bob = airutils.minmax(airutils.dot(accel,hull_direction),0.02) -- vertical bobbing + if bob < 0 then bob = 0 end + accel.y = accel.y + bob + local max_pitch = 6 + local ref_speed = longit_speed * 20 + if ref_speed < 0 then ref_speed = 0 end + local h_vel_compensation = ((ref_speed * 100)/max_pitch)/100 + if h_vel_compensation < 0 then h_vel_compensation = 0 end + if h_vel_compensation > max_pitch then h_vel_compensation = max_pitch end + --minetest.chat_send_all(h_vel_compensation) + newpitch = newpitch + (velocity.y * math.rad(max_pitch - h_vel_compensation)) + end + + local ceiling = 5000 + local blade_speed = self._rotor_speed or 15 + local limit = (self._max_speed) + if self._engine_running == false then + if is_flying then + blade_speed = self._rotor_idle_speed or 12 + if math.abs(longit_speed) > 0.5 then blade_speed = self._rotor_idle_speed + (self._rotor_speed - self._rotor_idle_speed) / 2 end + else + blade_speed = 0.001 --to avoid division by 0 + end + end + local new_accel = airutils.getLiftAccel(self, {x=0, y=velocity.y, z=blade_speed}, {x=0, y=accel.y, z=blade_speed/self.dtime}, blade_speed, roll, curr_pos, self._lift, ceiling, self._wing_span) + local y_accell = new_accel.y + new_accel = vector.new(accel) + new_accel.y = y_accell + if velocity.y > limit then new_accel.y = 0 end --it isn't a rocket :/ + + --wind effects + if airutils.wind and is_flying == true then + local wind = airutils.get_wind(curr_pos, 0.1) + new_accel = vector.add(new_accel, wind) + end + + if stop ~= true then --maybe == nil + self._last_accell = new_accel + self.object:move_to(curr_pos) + --airutils.set_acceleration(self.object, new_accel) + --local limit = self._climb_speed + --if new_accel.y > limit then new_accel.y = limit end --it isn't a rocket :/ + + else + if stop == true then + self._last_accell = vector.new() --self.object:get_acceleration() + self.object:set_acceleration({x=0,y=0,z=0}) + self.object:set_velocity({x=0,y=0,z=0}) + end + end + + if self.wheels then + if is_flying == false then --isn't flying? + --animate wheels + local min_speed_animation = 0.1 + if math.abs(velocity.x) > min_speed_animation or math.abs(velocity.z) > min_speed_animation then + self.wheels:set_animation_frame_speed(longit_speed * 10) + else + self.wheels:set_animation_frame_speed(0) + end + else + --stop wheels + self.wheels:set_animation_frame_speed(0) + end + end + + ------------------------------------------------------ + -- end accell + ------------------------------------------------------ + + ------------------------------------------------------ + -- sound and animation + ------------------------------------------------------ + local is_ship_attached = self.object:get_attach() + if is_ship_attached then + engine_set_sound_and_animation(self, false, newpitch, newroll) --is attached to a mother ship, so stop all + else + engine_set_sound_and_animation(self, is_flying, newpitch, newroll) + end + + ------------------------------------------------------ + + --GAUGES + --minetest.chat_send_all('rate '.. climb_rate) + local climb_angle = airutils.get_gauge_angle(climb_rate) + self._climb_rate = climb_rate + + local indicated_speed = longit_speed * 0.9 + if indicated_speed < 0 then indicated_speed = 0 end + self._indicated_speed = indicated_speed + local speed_angle = airutils.get_gauge_angle(indicated_speed, -45) + + --adjust power indicator + local fixed_power = 60 + if self._engine_running == false then fixed_power = 0 end + local power_indicator_angle = airutils.get_gauge_angle(fixed_power/10) + 90 + local fuel_in_percent = (self._energy * 1)/self._max_fuel + local energy_indicator_angle = (180*fuel_in_percent)-180 + + if is_attached then + if self._show_hud then + airutils.update_hud(player, climb_angle, speed_angle, power_indicator_angle, energy_indicator_angle) + else + airutils.remove_hud(player) + end + end + + if is_flying == false then + newyaw = yaw + end + + if player and self._use_camera_relocation then + --minetest.chat_send_all(dump(newroll)) + local new_eye_offset = airutils.camera_reposition(player, newpitch, newroll) + player:set_eye_offset(new_eye_offset, {x = 0, y = 1, z = -30}) + end + + --apply rotations + self.object:set_rotation({x=newpitch,y=newyaw,z=newroll}) + --end + + -- calculate energy consumption -- + airutils.consumptionCalc(self, accel) + + --saves last velocity for collision detection (abrupt stop) + self._last_accel = new_accel + self._last_vel = self.object:get_velocity() + self._last_longit_speed = longit_speed + self._yaw = newyaw + self._roll = newroll + self._pitch = newpitch +end diff --git a/mods/airutils/lib_copter/init.lua b/mods/airutils/lib_copter/init.lua new file mode 100644 index 00000000..8e59198f --- /dev/null +++ b/mods/airutils/lib_copter/init.lua @@ -0,0 +1,8 @@ + + +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_copter" .. DIR_DELIM .. "control.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_copter" .. DIR_DELIM .. "entities.lua") + + + + diff --git a/mods/airutils/lib_planes/control.lua b/mods/airutils/lib_planes/control.lua new file mode 100644 index 00000000..f9815d1d --- /dev/null +++ b/mods/airutils/lib_planes/control.lua @@ -0,0 +1,313 @@ +--global constants +airutils.ideal_step = 0.02 + +--[[airutils.rudder_limit = 30 +airutils.elevator_limit = 40]]-- + +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "utilities.lua") +local S = airutils.S + +function airutils.powerAdjust(self,dtime,factor,dir,max_power) + local max = max_power or 100 + local add_factor = factor/2 + add_factor = add_factor * (dtime/airutils.ideal_step) --adjusting the command speed by dtime + + if dir == 1 then + if self._power_lever < max then + self._power_lever = self._power_lever + add_factor + end + if self._power_lever > max then + self._power_lever = max + end + end + if dir == -1 then + if self._power_lever > 0 then + self._power_lever = self._power_lever - add_factor + if self._power_lever < 0 then self._power_lever = 0 end + end + if self._power_lever <= 0 then + self._power_lever = 0 + end + end +end + +function airutils.control(self, dtime, hull_direction, longit_speed, longit_drag, + later_speed, later_drag, accel, player, is_flying) + --if self.driver_name == nil then return end + local retval_accel = accel + + local stop = false + local ctrl = nil + + -- player control + if player then + ctrl = player:get_player_control() + + if ctrl.aux1 and self._last_time_command > 0.5 then + self._last_time_command = 0 + + end + ---------------------------------- + -- flap operation + ---------------------------------- + if ctrl.aux1 and ctrl.sneak and self._last_time_command >= 0.3 and self._wing_angle_extra_flaps then + self._last_time_command = 0 + airutils.flap_operate(self, player) + end + + self._acceleration = 0 + if self._engine_running then + --engine acceleration calc + local engineacc = (self._power_lever * self._max_engine_acc) / 100; + + local factor = 1 + + --increase power lever + if ctrl.jump then + airutils.powerAdjust(self, dtime, factor, 1) + end + --decrease power lever + if ctrl.sneak then + airutils.powerAdjust(self, dtime, factor, -1) + if self._power_lever <= 0 and is_flying == false then + --break + if longit_speed > 0 then + engineacc = -1 + if (longit_speed + engineacc) < 0 then + engineacc = longit_speed * -1 + end + end + if longit_speed < 0 then + engineacc = 1 + if (longit_speed + engineacc) > 0 then + engineacc = longit_speed * -1 + end + end + if math.abs(longit_speed) < 0.2 then + stop = true + end + end + end + --do not exceed + local max_speed = self._max_speed + if longit_speed > max_speed then + engineacc = engineacc - (longit_speed-max_speed) + if engineacc < 0 then engineacc = 0 end + end + self._acceleration = engineacc + else + local paddleacc = 0 + if longit_speed < 1.0 then + if ctrl.jump then paddleacc = 0.5 end + end + if longit_speed > -1.0 then + if ctrl.sneak then paddleacc = -0.5 end + end + self._acceleration = paddleacc + end + + local hull_acc = vector.multiply(hull_direction,self._acceleration) + retval_accel=vector.add(retval_accel,hull_acc) + + --pitch + local pitch_cmd = 0 + if self._yaw_by_mouse == true then + airutils.set_pitch_by_mouse(self, player) + else + if ctrl.up then pitch_cmd = 1 elseif ctrl.down then pitch_cmd = -1 end + airutils.set_pitch(self, pitch_cmd, dtime) + end + + -- yaw + local yaw_cmd = 0 + if self._yaw_by_mouse == true then + local rot_y = math.deg(player:get_look_horizontal()) + airutils.set_yaw_by_mouse(self, rot_y) + else + if ctrl.right then yaw_cmd = 1 elseif ctrl.left then yaw_cmd = -1 end + airutils.set_yaw(self, yaw_cmd, dtime) + end + + --I'm desperate, center all! + if ctrl.right and ctrl.left then + self._elevator_angle = 0 + self._rudder_angle = 0 + end + + if ctrl.up and ctrl.down and self._last_time_command > 0.5 then + self._last_time_command = 0 + local name = player:get_player_name() + if self._yaw_by_mouse == true then + minetest.chat_send_player(name, core.colorize('#0000ff', S(" >>> Mouse control disabled."))) + self._yaw_by_mouse = false + else + minetest.chat_send_player(name, core.colorize('#0000ff', S(" >>> Mouse control enabled."))) + self._yaw_by_mouse = true + end + end + end + + if longit_speed > 0 then + if ctrl then + if not (ctrl.right or ctrl.left) then + airutils.rudder_auto_correction(self, longit_speed, dtime) + end + else + airutils.rudder_auto_correction(self, longit_speed, dtime) + end + if airutils.elevator_auto_correction then + self._elevator_angle = airutils.elevator_auto_correction(self, longit_speed, self.dtime, self._max_speed, self._elevator_angle, self._elevator_limit, airutils.ideal_step, 100) + end + end + + return retval_accel, stop +end + +function airutils.set_pitch_by_mouse(self, player) + local vehicle_rot = self.object:get_rotation() + local rot_x = player:get_look_vertical()-vehicle_rot.x + self._elevator_angle = -(rot_x * self._elevator_limit)*(self._pitch_intensity*10) + if self._elevator_angle > self._elevator_limit then self._elevator_angle = self._elevator_limit end + if self._elevator_angle < -self._elevator_limit then self._elevator_angle = -self._elevator_limit end +end + +function airutils.set_pitch(self, dir, dtime) + local pitch_factor = self._pitch_intensity or 0.6 + local multiplier = pitch_factor*(dtime/airutils.ideal_step) + if dir == -1 then + --minetest.chat_send_all("cabrando") + self._elevator_angle = math.max(self._elevator_angle-multiplier,-self._elevator_limit) + elseif dir == 1 then + --minetest.chat_send_all("picando") + self._elevator_angle = math.min(self._elevator_angle+multiplier,self._elevator_limit) + end +end + +function airutils.set_autopilot_pitch(self, dir, dtime) + local pitch_factor = 0.05 + local multiplier = pitch_factor*(dtime/airutils.ideal_step) + if dir == -1 then + --minetest.chat_send_all("cabrando") + self._elevator_angle = math.max(self._elevator_angle-multiplier,-self._elevator_limit) + elseif dir == 1 then + --minetest.chat_send_all("picando") + self._elevator_angle = math.min(self._elevator_angle+multiplier,self._elevator_limit) + end +end + +function airutils.set_yaw_by_mouse(self, dir) + local rotation = self.object:get_rotation() + local rot_y = math.deg(rotation.y) + + local total = math.abs(math.floor(rot_y/360)) + + if rot_y < 0 then rot_y = rot_y + (360*total) end + if rot_y > 360 then rot_y = rot_y - (360*total) end + if rot_y >= 270 and dir <= 90 then dir = dir + 360 end + if rot_y <= 90 and dir >= 270 then dir = dir - 360 end + + local intensity = self._yaw_intensity / 10 + local command = (rot_y - dir) * intensity + if command < -90 then command = -90 + elseif command > 90 then command = 90 end + --minetest.chat_send_all("rotation y: "..rot_y.." - dir: "..dir.." - command: "..command) + + self._rudder_angle = (-command * self._rudder_limit)/90 +end + +function airutils.set_yaw(self, dir, dtime) + local yaw_factor = self._yaw_intensity or 25 + if dir == 1 then + self._rudder_angle = math.max(self._rudder_angle-(yaw_factor*dtime),-self._rudder_limit) + elseif dir == -1 then + self._rudder_angle = math.min(self._rudder_angle+(yaw_factor*dtime),self._rudder_limit) + end +end + +function airutils.rudder_auto_correction(self, longit_speed, dtime) + local factor = 1 + if self._rudder_angle > 0 then factor = -1 end + local correction = (self._rudder_limit*(longit_speed/1000)) * factor * (dtime/airutils.ideal_step) + local before_correction = self._rudder_angle + local new_rudder_angle = self._rudder_angle + correction + if math.sign(before_correction) ~= math.sign(new_rudder_angle) then + self._rudder_angle = 0 + else + self._rudder_angle = new_rudder_angle + end +end + +function airutils.autopilot(self, dtime, hull_direction, longit_speed, accel, curr_pos) + + local retval_accel = accel + + if not self._have_auto_pilot then return end + + local min_attack_angle = self._wing_angle_of_attack or 1.0 + local flap = self._wing_angle_extra_flaps or 2 + local max_attack_angle = min_attack_angle + flap --1.8 + + --climb + local velocity = self.object:get_velocity() + local climb_rate = velocity.y * 1.5 + if climb_rate > 5 then climb_rate = 5 end + if climb_rate < -5 then + climb_rate = -5 + end + + self._acceleration = 0 + local climb_rate_min = 0.2 + local factor = math.abs(climb_rate * 0.1) + if self._engine_running then + --engine acceleration calc + local engineacc = (self._power_lever * self._max_engine_acc) / 100; + --self.engine:set_animation_frame_speed(60 + self._power_lever) + + --increase power lever + if climb_rate > climb_rate_min then + airutils.powerAdjust(self, dtime, factor, -1) + end + --decrease power lever + if climb_rate < 0 then + airutils.powerAdjust(self, dtime, factor, 1) + end + --do not exceed + local max_speed = self._max_speed + if longit_speed > max_speed then + engineacc = 0 + if engineacc < 0 then engineacc = 0 end + end + self._acceleration = engineacc + end + + local hull_acc = vector.multiply(hull_direction,self._acceleration) + retval_accel=vector.add(retval_accel,hull_acc) + + --decrease power lever + if climb_rate < 0 then + airutils.set_autopilot_pitch(self, -1, dtime) + --core.chat_send_all("cabrando: "..dump(climb_rate)) + elseif climb_rate > climb_rate_min then + airutils.set_autopilot_pitch(self, 1, dtime) + --core.chat_send_all("picando: "..dump(climb_rate)) + end + + --pitch + --[[if self._angle_of_attack > max_attack_angle then + airutils.set_autopilot_pitch(self, -1, dtime) + elseif self._angle_of_attack < min_attack_angle then + airutils.set_autopilot_pitch(self, 1, dtime) + end]]-- + + -- yaw + airutils.set_yaw(self, 0, dtime) + + if longit_speed > (self._min_speed or 0) then + airutils.rudder_auto_correction(self, longit_speed, dtime) + if airutils.elevator_auto_correction then + --self._elevator_angle = airutils.elevator_auto_correction(self, longit_speed, self.dtime, self._max_speed, self._elevator_angle, self._elevator_limit, airutils.ideal_step, 500) + end + end + + return retval_accel +end diff --git a/mods/airutils/lib_planes/custom_physics.lua b/mods/airutils/lib_planes/custom_physics.lua new file mode 100644 index 00000000..1c87eb87 --- /dev/null +++ b/mods/airutils/lib_planes/custom_physics.lua @@ -0,0 +1,114 @@ +function airutils.physics(self) + local friction = self._ground_friction or 0.99 + local vel=self.object:get_velocity() + local new_velocity = vector.new() + + --buoyancy + local surface = nil + local surfnodename = nil + local spos = airutils.get_stand_pos(self) + if not spos then return end + spos.y = spos.y+0.01 + -- get surface height + local snodepos = airutils.get_node_pos(spos) + local surfnode = airutils.nodeatpos(spos) + while surfnode and (surfnode.drawtype == 'liquid' or surfnode.drawtype == 'flowingliquid') do + surfnodename = surfnode.name + surface = snodepos.y +0.5 + if surface > spos.y+self.height then break end + snodepos.y = snodepos.y+1 + surfnode = airutils.nodeatpos(snodepos) + end + + self.isinliquid = surfnodename + if surface then -- standing in liquid + self.isinliquid = true + end + local last_accel = vector.new() + if self._last_accel then + last_accel = vector.new(self._last_accel) + end + + if self.isinliquid then + self.water_drag = 0.2 + self.isinliquid = true + local height = self.height + local submergence = math.min(surface-spos.y,height)/height +-- local balance = self.buoyancy*self.height + local buoyacc = airutils.gravity*(self.buoyancy-submergence) + --local buoyacc = self._baloon_buoyancy*(self.buoyancy-submergence) + local accell = { + x=-vel.x*self.water_drag, + y=buoyacc-(vel.y*math.abs(vel.y)*0.4), + z=-vel.z*self.water_drag + } + if self.buoyancy >= 1 then self._engine_running = false end + if last_accel then + accell = vector.add(accell,last_accel) + end + new_velocity = vector.multiply(accell,self.dtime) + --airutils.set_acceleration(self.object,accell) + --self.object:move_to(self.object:get_pos()) + else + --airutils.set_acceleration(self.object,{x=0,y=airutils.gravity,z=0}) + self.isinliquid = false + + if last_accel then + last_accel.y = last_accel.y + airutils.gravity --gravity here + + new_velocity = vector.multiply(last_accel,self.dtime) + end + --self.object:set_acceleration({x=0,y=new_accel.y, z=0}) + end + + if self.isonground and not self.isinliquid then + --dumb friction + new_velocity = {x=new_velocity.x*friction, + y=new_velocity.y, + z=new_velocity.z*friction} + + -- bounciness + if self.springiness and self.springiness > 0 and self.buoyancy >= 1 then + local vnew = vector.new(new_velocity) + + if not self.collided then -- ugly workaround for inconsistent collisions + for _,k in ipairs({'y','z','x'}) do + if new_velocity[k]==0 and math.abs(self.lastvelocity[k])> 0.1 then + vnew[k]=-self.lastvelocity[k]*self.springiness + end + end + end + + if not vector.equals(new_velocity,vnew) then + self.collided = true + else + if self.collided then + vnew = vector.new(self.lastvelocity) + end + self.collided = false + end + new_velocity = vnew + end + + --damage if the friction is below .97 + if self._last_longit_speed then + if friction <= 0.97 and self._last_longit_speed > 0 then + self.hp_max = self.hp_max - 0.001 + airutils.setText(self, self._vehicle_name) + end --damage the plane if it have hard friction + end + + --self.object:set_velocity(new_velocity) + --new_velocity = vector.subtract(new_velocity,vel) + + --fix bug with unexpe3cted moving + if not self.driver_name and math.abs(vel.x) < 0.2 and math.abs(vel.z) < 0.2 then + self.object:set_velocity({x=0,y=airutils.gravity*self.dtime,z=0}) + if self.wheels then self.wheels:set_animation_frame_speed(0) end + return + end + end + + self.object:add_velocity(new_velocity) +end + diff --git a/mods/airutils/lib_planes/entities.lua b/mods/airutils/lib_planes/entities.lua new file mode 100644 index 00000000..b7d7703f --- /dev/null +++ b/mods/airutils/lib_planes/entities.lua @@ -0,0 +1,1033 @@ +dofile(core.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") + +local S = airutils.S + +local function lib_change_color(self, colstr) + airutils.param_paint(self, colstr) +end + +function airutils.get_staticdata(self) -- unloaded/unloads ... is now saved + return core.serialize({ + --stored_sound_handle = self.sound_handle, + stored_energy = self._energy or 0, + stored_owner = self.owner or "", + stored_hp = self.hp_max or 10, + stored_color = self._color or "#FFFFFF", + stored_color_2 = self._color_2 or "#FFFFFF", + stored_power_lever = self._power_lever or 0, + stored_driver_name = self.driver_name or nil, + stored_last_accell = self._last_accell or vector.new(), + stored_inv_id = self._inv_id or nil, + stored_flap = self._flap or false, + stored_passengers = self._passengers or {}, + stored_adf_destiny = self._adf_destiny or vector.new(), + stored_skin = self._skin or "", + stored_vehicle_custom_data = self._vehicle_custom_data or nil, + stored_ship_name = self._ship_name or "", + remove = self._remove or false, + }) +end + +function airutils.on_deactivate(self) + airutils.save_inventory(self) + local pos = self.object:get_pos() + if airutils.debug_log then + core.log("action","deactivating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) + end +end + +function airutils.on_activate(self, staticdata, dtime_s) + if not self.object then return end + local pos = self.object:get_pos() + airutils.actfunc(self, staticdata, dtime_s) + self._flap = false + + if staticdata ~= "" and staticdata ~= nil then + local data = core.deserialize(staticdata) or {} + self._energy = data.stored_energy or 0 + self.owner = data.stored_owner or "" + self.hp_max = data.stored_hp or 10 + self._color = data.stored_color or "#FFFFFF" + self._color_2 = data.stored_color_2 or data.stored_color --if it has no color 2, now it have! + self._power_lever = data.stored_power_lever or 0 + self.driver_name = data.stored_driver_name or nil + self._last_accell = data.stored_last_accell or vector.new() + self._inv_id = data.stored_inv_id or nil + if self._wing_angle_extra_flaps then self._flap = data.stored_flap or false end + self._passengers = data.stored_passengers or {} + self._adf_destiny = data.stored_adf_destiny or vector.new() + self._skin = data.stored_skin or "" + self._ship_name = data.stored_ship_name or "" + self._remove = data.remove or false + local custom_data = data.stored_vehicle_custom_data or nil + if custom_data then + self._vehicle_custom_data = custom_data + else + -- o macete aqui eh inicializar mesmo que não exista no escopo da entity + self._vehicle_custom_data = {} --initialize it + end + --core.debug("loaded: ", self._energy) + if self._engine_running then + self._last_applied_power = -1 --signal to start + end + + if self._remove == true then + airutils.destroy_inventory(self) + self.object:remove() + return + end + end + + self._climb_rate = 0 + self._yaw = 0 + self._roll = 0 + self._pitch = 0 + + if airutils.debug_log then + core.log("action","activating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) + end + + if self._register_parts_method then + self._register_parts_method(self) + end + + airutils.param_paint(self, self._color, self._color_2) + + self.object:set_armor_groups({immortal=1}) + + local start_frame = 1 + local end_frame = self._anim_frames + if self._anim_start_frame then + start_frame = self._anim_start_frame + end_frame = self._anim_start_frame + self._anim_frames + end + + self.object:set_animation({x = start_frame, y = end_frame}, 0, 0, true) + if self.wheels then + self.wheels:set_animation({x = 1, y = self._anim_frames}, 0, 0, true) + end + + local inv = nil + if self._inv_id then + inv = core.get_inventory({type = "detached", name = self._inv_id}) + end + -- if the game was closed the inventories have to be made anew, instead of just reattached + if not inv then + airutils.create_inventory(self, self._trunk_slots) + else + self._inv = inv + end + + --airutils.seats_create(self) + self._passengers = {} + if not self._vehicle_custom_data then self._vehicle_custom_data = {} end --initialize when it does not exists + + if self._flap then airutils.flap_on(self) end + + if self._vehicle_name then airutils.setText(self, self._vehicle_name) end + + self._change_color = lib_change_color + + --initialize array with seats + airutils.seats_create(self) +end + +function airutils.on_step(self,dtime,colinfo) + self.dtime = math.min(dtime,0.2) + self.colinfo = colinfo + self.height = airutils.get_box_height(self) + + if not self.object then return end + +-- physics comes first + local vel = self.object:get_velocity() + local pos = self.object:get_pos() + local props = self.object:get_properties() + +-- handle visibility on radar + if (pos and pos.y < airutils.radarMinHeight and props.show_on_minimap) then + props.show_on_minimap = false + self.object:set_properties(props) + end + if (pos and pos.y >= airutils.radarMinHeight and not props.show_on_minimap) then + props.show_on_minimap = true + self.object:set_properties(props) + end + if self.isonground and props.show_on_minimap then + props.show_on_minimap = false + self.object:set_properties(props) + end + + if colinfo then + self.isonground = colinfo.touching_ground + else + if self.lastvelocity.y==0 and vel.y==0 then + self.isonground = true + else + self.isonground = false + end + end + + if self.hp_max then + if self.hp_max <= 0 then + airutils.destroy(self) + end + end + + self:physics() + + if self.logic then + self:logic() + end + + self.lastvelocity = self.object:get_velocity() + self.time_total=self.time_total+self.dtime +end + +local function ground_pitch(self, longit_speed, curr_pitch) + local newpitch = curr_pitch + if self._last_longit_speed == nil then self._last_longit_speed = 0 end + + -- Estado atual do sistema + if self._current_value == nil then self._current_value = 0 end -- Valor atual do sistema + if self._last_error == nil then self._last_error = 0 end -- Último erro registrado + + -- adjust pitch at ground + if math.abs(longit_speed) < self._tail_lift_max_speed then + local speed_range = self._tail_lift_max_speed - self._tail_lift_min_speed + local percentage = 1-((math.abs(longit_speed) - self._tail_lift_min_speed)/speed_range) + if percentage > 1 then percentage = 1 end + if percentage < 0 then percentage = 0 end + local angle = self._tail_angle * percentage + local rad_angle = math.rad(angle) + + if newpitch < rad_angle then newpitch = rad_angle end --ja aproveita o pitch atual se ja estiver cerrto + --[[self._current_value = curr_pitch + local kp = (longit_speed - self._tail_lift_min_speed)/10 + local output, last_error = airutils.pid_controller(self._current_value, rad_angle, self._last_error, self.dtime, kp) + self._last_error = last_error + newpitch = output]]-- + + if newpitch > math.rad(self._tail_angle) then newpitch = math.rad(self._tail_angle) end --não queremos arrastar o cauda no chão + end + + return newpitch +end + +function airutils.logic(self) + local velocity = self.object:get_velocity() + local rem_obj = self.object:get_attach() + local extern_ent = nil + if rem_obj then + extern_ent = rem_obj:get_luaentity() + end + local curr_pos = self.object:get_pos() + self._curr_pos = curr_pos --shared + self._last_accel = self.object:get_acceleration() + + self._last_time_command = self._last_time_command + self.dtime + + if self._last_time_command > 1 then self._last_time_command = 1 end + + local player = nil + if self.driver_name then player = core.get_player_by_name(self.driver_name) end + local co_pilot = nil + if self.co_pilot and self._have_copilot then co_pilot = core.get_player_by_name(self.co_pilot) end + + --test collision + airutils.testImpact(self, velocity, curr_pos) + + --if self._autoflymode == true then airutils.seats_update(self) end + + if player then + local ctrl = player:get_player_control() + --------------------- + -- change the driver + --------------------- + if co_pilot and self._have_copilot and self._last_time_command >= 1 then + if self._command_is_given == true then + if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then + self._last_time_command = 0 + --take the control + airutils.transfer_control(self, false) + end + else + if ctrl.sneak == true and ctrl.jump == true then + self._last_time_command = 0 + --trasnfer the control to student + airutils.transfer_control(self, true) + end + end + end + ----------- + --autopilot + ----------- + if self._instruction_mode == false and self._last_time_command >= 1 then + if self._autopilot == true then + if ctrl.sneak or ctrl.jump or ctrl.up or ctrl.down or ctrl.right or ctrl.left then + self._last_time_command = 0 + self._autopilot = false + core.chat_send_player(self.driver_name,S(" >>> Autopilot deactivated")) + end + else + if ctrl.sneak == true and ctrl.jump == true and self._have_auto_pilot then + self._last_time_command = 0 + self._autopilot = true + self._auto_pilot_altitude = curr_pos.y + core.chat_send_player(self.driver_name,core.colorize('#00ff00', S(" >>> Autopilot activated"))) + if self.driver_name then + core.sound_play("airutils_beep", { + to_player = self.driver_name, + gain = 1.0, + fade = 0.0, + pitch = 0.7, + }, true) + end + end + end + end + end + + if not self.object:get_acceleration() then return end + local accel_y = self.object:get_acceleration().y + local rotation = self.object:get_rotation() + local yaw = rotation.y + local newyaw=yaw + local roll = rotation.z + local newroll=roll + newroll = math.floor(newroll/360) + newroll = newroll * 360 + + local hull_direction = airutils.rot_to_dir(rotation) --core.yaw_to_dir(yaw) + local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector + + local longit_speed = vector.dot(velocity,hull_direction) + + if extern_ent then + if extern_ent.curr_speed then longit_speed = extern_ent.curr_speed end + --core.chat_send_all(dump(longit_speed)) + end + + self._longit_speed = longit_speed + local longit_drag = vector.multiply(hull_direction,longit_speed* + longit_speed*self._longit_drag_factor*-1*airutils.sign(longit_speed)) + local later_speed = airutils.dot(velocity,nhdir) + --core.chat_send_all('later_speed: '.. later_speed) + local later_drag = vector.multiply(nhdir,later_speed*later_speed* + self._later_drag_factor*-1*airutils.sign(later_speed)) + local accel = vector.add(longit_drag,later_drag) + local stop = false + + local is_flying = true + if self.colinfo then + is_flying = (not self.colinfo.touching_ground) and (self.isinliquid == false) + else + --special routine for automated plane + if extern_ent then + if not extern_ent.on_rightclick then + local touch_point = (self.initial_properties.collisionbox[2])-0.5 + local node_bellow = airutils.nodeatpos(airutils.pos_shift(curr_pos,{y=touch_point})) + --core.chat_send_all(dump(node_bellow.drawtype)) + if (node_bellow and node_bellow.drawtype ~= 'airlike') then + is_flying = false + end + end + end + end + --core.chat_send_all(dump(is_flying)) + --if is_flying then core.chat_send_all('is flying') end + + local is_attached = airutils.checkAttach(self, player) + if self._indicated_speed == nil then self._indicated_speed = 0 end + + -- for some engine error the player can be detached from the machine, so lets set him attached again + airutils.checkattachBug(self) + + + if self._custom_step_additional_function then + self._custom_step_additional_function(self) + end + + --fix old planes + if not self._flap then self._flap = false end + if not self._wing_configuration then self._wing_configuration = self._wing_angle_of_attack end + + + if self._wing_configuration == self._wing_angle_of_attack and self._flap then + airutils.flap_on(self) + end + if self._wing_configuration ~= self._wing_angle_of_attack and self._flap == false then + airutils.flap_off(self) + end + + --landing light + if self._have_landing_lights then + airutils.landing_lights_operate(self) + end + + --smoke and fire + if self._engine_running then + local curr_health_percent = (self.hp_max * 100)/self._max_plane_hp + if curr_health_percent < 20 then + airutils.add_smoke_trail(self, 2) + elseif curr_health_percent < 50 then + airutils.add_smoke_trail(self, 1) + end + else + if self._smoke_spawner and not self._smoke_semaphore then + self._smoke_semaphore = 1 --to set it only one time + core.after(5, function() + if self._smoke_spawner then + core.delete_particlespawner(self._smoke_spawner) + self._smoke_spawner = nil + self._smoke_semaphore = nil + end + end) + end + end + + --adjust elevator pitch (3d model) + self.object:set_bone_position("elevator", self._elevator_pos, {x=-self._elevator_angle*2 - 90, y=0, z=0}) + --adjust rudder + self.object:set_bone_position("rudder", self._rudder_pos, {x=0,y=self._rudder_angle,z=0}) + --adjust ailerons + if self._aileron_r_pos and self._aileron_l_pos then + local ailerons = self._rudder_angle + if self._invert_ailerons then ailerons = ailerons * -1 end + self.object:set_bone_position("aileron.r", self._aileron_r_pos, {x=-ailerons - 90,y=0,z=0}) + self.object:set_bone_position("aileron.l", self._aileron_l_pos, {x=ailerons - 90,y=0,z=0}) + end + + if (math.abs(velocity.x) < 0.1 and math.abs(velocity.z) < 0.1) and is_flying == false and is_attached == false and self._engine_running == false then + if self._ground_friction then + if not self.isinliquid then self.object:set_velocity({x=0,y=airutils.gravity*self.dtime,z=0}) end + end + return + end + + --adjust climb indicator + local y_velocity = 0 + if self._engine_running or is_flying then y_velocity = velocity.y end + local climb_rate = y_velocity + if climb_rate > 5 then climb_rate = 5 end + if climb_rate < -5 then + climb_rate = -5 + end + + -- pitch + local newpitch = airutils.get_plane_pitch(y_velocity, longit_speed, self._min_speed, self._angle_of_attack) + + --for airplanes with cannard or pendulum wing + local actuator_angle = self._elevator_angle + if self._inverted_pitch_reaction then actuator_angle = -1*self._elevator_angle end + + --is an stall, force a recover + if longit_speed < (self._min_speed+0.5) and climb_rate < -1.5 and is_flying then + --[[ + if player and self.driver_name then + core.chat_send_player(self.driver_name,core.colorize('#ff0000', " >>> STALL")) + end + ]]-- + self._elevator_angle = 0 + self._angle_of_attack = -1 + newpitch = math.rad(self._angle_of_attack) + else + --ajustar angulo de ataque + if longit_speed > self._min_speed then + local percentage = math.abs(((longit_speed * 100)/(self._min_speed + 5))/100) + if percentage > 1.5 then percentage = 1.5 end + + self._angle_of_attack = self._wing_angle_of_attack - ((actuator_angle / self._elevator_response_attenuation)*percentage) + + --airutils.adjust_attack_angle_by_speed(angle_of_attack, min_angle, max_angle, limit, longit_speed, ideal_step, dtime) + self._angle_of_attack = airutils.adjust_attack_angle_by_speed(self._angle_of_attack, self._min_attack_angle, self._max_attack_angle, 40, longit_speed, airutils.ideal_step, self.dtime) + + if self._angle_of_attack < self._min_attack_angle then + self._angle_of_attack = self._min_attack_angle + actuator_angle = actuator_angle - 0.2 + end --limiting the negative angle]]-- + --[[if self._angle_of_attack > self._max_attack_angle then + self._angle_of_attack = self._max_attack_angle + actuator_angle = actuator_angle + 0.2 + end --limiting the very high climb angle due to strange behavior]]--]]-- + + if self._inverted_pitch_reaction then self._elevator_angle = -1*actuator_angle end --revert the reversion + + end + end + + --core.chat_send_all(self._angle_of_attack) + + -- adjust pitch at ground + if math.abs(longit_speed) > self._tail_lift_min_speed and is_flying == false then + newpitch = ground_pitch(self, longit_speed, newpitch) + else + if math.abs(longit_speed) < self._tail_lift_min_speed then + newpitch = math.rad(self._tail_angle) + end + end + + -- new yaw + if math.abs(self._rudder_angle)>1.5 then + local turn_rate = math.rad(self._yaw_turn_rate) + local yaw_turn = self.dtime * math.rad(self._rudder_angle) * turn_rate * + airutils.sign(longit_speed) * math.abs(longit_speed/2) + newyaw = yaw + yaw_turn + end + + --roll adjust + --------------------------------- + local delta = 0.002 + if is_flying then + local roll_reference = newyaw + local sdir = core.yaw_to_dir(roll_reference) + local snormal = {x=sdir.z,y=0,z=-sdir.x} -- rightside, dot is negative + local prsr = airutils.dot(snormal,nhdir) + local rollfactor = -90 + local roll_rate = math.rad(10) + newroll = (prsr*math.rad(rollfactor)) * (later_speed * roll_rate) * airutils.sign(longit_speed) + + --[[local rollRotation = -self._rudder_angle * 0.1 + newroll = rollRotation]]-- + + --core.chat_send_all('newroll: '.. newroll) + else + delta = 0.2 + if roll > 0 then + newroll = roll - delta + if newroll < 0 then newroll = 0 end + end + if roll < 0 then + newroll = roll + delta + if newroll > 0 then newroll = 0 end + end + end + + --------------------------------- + -- end roll + + local pilot = player + if self._have_copilot then + if self._command_is_given and co_pilot then + pilot = co_pilot + else + self._command_is_given = false + end + end + + ------------------------------------------------------ + --accell calculation block + ------------------------------------------------------ + if is_attached or co_pilot then + if self._autopilot ~= true then + accel, stop = airutils.control(self, self.dtime, hull_direction, + longit_speed, longit_drag, later_speed, later_drag, accel, pilot, is_flying) + else + accel = airutils.autopilot(self, self.dtime, hull_direction, longit_speed, accel, curr_pos) + end + end + --end accell + + --get disconnected players + if self._autoflymode ~= true then + airutils.rescueConnectionFailedPassengers(self) + end + + if accel == nil then accel = {x=0,y=0,z=0} end + + --lift calculation + accel.y = accel_y + + --lets apply some bob in water + if self.isinliquid then + local bob = airutils.minmax(airutils.dot(accel,hull_direction),0.02) -- vertical bobbing + if bob < 0 then bob = 0 end + accel.y = accel.y + bob + local max_pitch = 6 + local ref_speed = longit_speed * 20 + if ref_speed < 0 then ref_speed = 0 end + local h_vel_compensation = ((ref_speed * 100)/max_pitch)/100 + if h_vel_compensation < 0 then h_vel_compensation = 0 end + if h_vel_compensation > max_pitch then h_vel_compensation = max_pitch end + --core.chat_send_all(h_vel_compensation) + newpitch = newpitch + (velocity.y * math.rad(max_pitch - h_vel_compensation)) + + if airutils.use_water_particles == true and airutils.add_splash and self._splash_x_position and self.buoyancy then + local splash_frequency = 0.15 + if self._last_splash == nil then self._last_splash = 0.5 else self._last_splash = self._last_splash + self.dtime end + if longit_speed >= 2.0 and self._last_vel and self._last_splash >= splash_frequency then + self._last_splash = 0 + local splash_pos = vector.new(curr_pos) + local bellow_position = self.initial_properties.collisionbox[2] + local collision_height = self.initial_properties.collisionbox[5] - bellow_position + splash_pos.y = splash_pos.y + (bellow_position + (collision_height * self.buoyancy)) - (collision_height/10) + airutils.add_splash(splash_pos, newyaw, self._splash_x_position) + end + end + end + + local new_accel = accel + if longit_speed > self._min_speed*0.66 then + --[[lets do something interesting: + here I'll fake the longit speed effect for takeoff, to force the airplane + to use more runway + ]]-- + local factorized_longit_speed = longit_speed + if is_flying == false and airutils.quadBezier then + local takeoff_speed = self._min_speed * 4 --so first I'll consider the takeoff speed 4x the minimal flight speed + if longit_speed < takeoff_speed and longit_speed > self._min_speed then -- then if the airplane is above the mininam speed and bellow the take off + local scale = (longit_speed*1)/takeoff_speed --get a scale of current longit speed relative to takeoff speed + if scale == nil then scale = 0 end --lets avoid any nil + factorized_longit_speed = airutils.quadBezier(scale, self._min_speed, longit_speed, longit_speed) --here the magic happens using a bezier curve + --core.chat_send_all("factor: " .. factorized_longit_speed .. " - longit: " .. longit_speed .. " - scale: " .. scale) + if factorized_longit_speed < 0 then factorized_longit_speed = 0 end --lets avoid negative numbers + if factorized_longit_speed == nil then factorized_longit_speed = longit_speed end --and nil numbers + end + end + + local ceiling = 15000 + new_accel = airutils.getLiftAccel(self, velocity, new_accel, factorized_longit_speed, roll, curr_pos, self._lift, ceiling, self._wing_span) + end + -- end lift + + --wind effects + if longit_speed > 1.5 and airutils.wind then + local wind = airutils.get_wind(curr_pos, 0.1) + new_accel = vector.add(new_accel, wind) + end + + if stop ~= true then --maybe == nil + self._last_accell = new_accel + self.object:move_to(curr_pos) + --airutils.set_acceleration(self.object, new_accel) + local limit = (self._max_speed/self.dtime) + if new_accel.y > limit then new_accel.y = limit end --it isn't a rocket :/ + + else + if stop == true then + self._last_accell = vector.new() --self.object:get_acceleration() + self.object:set_acceleration({x=0,y=0,z=0}) + self.object:set_velocity({x=0,y=0,z=0}) + end + end + + if self.wheels then + if is_flying == false then --isn't flying? + --animate wheels + local min_speed_animation = 0.1 + if math.abs(velocity.x) > min_speed_animation or math.abs(velocity.z) > min_speed_animation then + self.wheels:set_animation_frame_speed(longit_speed * 10) + else + if extern_ent then + self.wheels:set_animation_frame_speed(longit_speed * 10) + else + self.wheels:set_animation_frame_speed(0) + end + end + else + --stop wheels + self.wheels:set_animation_frame_speed(0) + end + end + + ------------------------------------------------------ + -- end accell + ------------------------------------------------------ + + ------------------------------------------------------ + -- sound and animation + ------------------------------------------------------ + airutils.engine_set_sound_and_animation(self) + + ------------------------------------------------------ + + --self.object:get_luaentity() --hack way to fix jitter on climb + + --GAUGES + --core.chat_send_all('rate '.. climb_rate) + local climb_angle = airutils.get_gauge_angle(climb_rate) + self._climb_rate = climb_rate + + local indicated_speed = longit_speed * 0.9 + if indicated_speed < 0 then indicated_speed = 0 end + self._indicated_speed = indicated_speed + local speed_angle = airutils.get_gauge_angle(indicated_speed, -45) + + --adjust power indicator + local power_indicator_angle = airutils.get_gauge_angle(self._power_lever/10) + 90 + local fuel_in_percent = (self._energy * 1)/self._max_fuel + local energy_indicator_angle = (180*fuel_in_percent)-180 --(airutils.get_gauge_angle((self._max_fuel - self._energy)*2)) - 90 + + if is_attached then + if self._show_hud then + airutils.update_hud(player, climb_angle, speed_angle, power_indicator_angle, energy_indicator_angle) + else + airutils.remove_hud(player) + end + end + + if is_flying == false then + -- new yaw + local turn_rate = math.rad(30) + local yaw_turn = self.dtime * math.rad(self._rudder_angle) * turn_rate * + airutils.sign(longit_speed) * math.abs(longit_speed/2) + newyaw = yaw + yaw_turn + end + + if player and self._use_camera_relocation then + --core.chat_send_all(dump(newroll)) + local new_eye_offset = airutils.camera_reposition(player, newpitch, newroll) + player:set_eye_offset(new_eye_offset, {x = 0, y = 1, z = -30}) + end + + --apply rotations + self.object:set_rotation({x=newpitch,y=newyaw,z=newroll}) + --end + + if (longit_speed / 2) > self._max_speed and self._flap == true then + if is_attached and self.driver_name then + core.chat_send_player(self.driver_name, core.colorize('#ff0000', S(" >>> Flaps retracted due for overspeed"))) + end + self._flap = false + end + + -- calculate energy consumption -- + airutils.consumptionCalc(self, accel) + + --saves last velocity for collision detection (abrupt stop) + self._last_accel = new_accel + self._last_vel = self.object:get_velocity() + self._last_longit_speed = longit_speed + self._yaw = newyaw + self._roll = newroll + self._pitch = newpitch +end + +local function damage_vehicle(self, toolcaps, ttime, damage) + damage = damage or 0 + if (not toolcaps) then + return + end + local value = toolcaps.damage_groups.fleshy or 0 + if (toolcaps.damage_groups.vehicle) then + value = toolcaps.damage_groups.vehicle + end + damage = damage + value / 10 + self.hp_max = self.hp_max - damage + if self.hp_max < 0 then self.hp_max = 0 end + airutils.setText(self, self._vehicle_name) +end + +function airutils.on_punch(self, puncher, ttime, toolcaps, dir, damage) + local name = "" + local ppos = {} + + if not puncher or not puncher:is_player() then + return + end + + if (puncher:is_player()) then + name = puncher:get_player_name() + ppos = puncher:get_pos() + if (core.is_protected(ppos, name) and + airutils.protect_in_areas) then + return + end + end + + if self.hp_max <= 0 then + airutils.destroy(self, name) + return + end + if self._vehicle_name then airutils.setText(self, self._vehicle_name) end + + if (string.find(puncher:get_wielded_item():get_name(), "rayweapon") or + toolcaps.damage_groups.vehicle) then + damage_vehicle(self, toolcaps, ttime, damage) + end + + local is_admin = core.check_player_privs(puncher, {server=true}) + if self.owner == nil then + self.owner = name + end + if self.owner and self.owner ~= name and self.owner ~= "" then + if is_admin == false then return end + end + + if is_admin == false and core.check_player_privs(puncher, {protection_bypass=false}) then + if self.driver_name and self.driver_name ~= name then + -- do not allow other players to remove the object while there is a driver + return + end + end + + local is_attached = false + local player_attach = puncher:get_attach() + if player_attach then + if player_attach ~= self.object then + local slot_attach = player_attach:get_attach() + if slot_attach == self.object then is_attached = true end + else + is_attached = true + end + end + + if puncher:get_attach() == self.object then is_attached = true end + --if puncher:get_attach() == self.pilot_seat_base then is_attached = true end + + local itmstck=puncher:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + if is_attached == false then + if airutils.loadFuel(self, puncher:get_player_name()) then + return + end + + --repair + if (item_name == "airutils:repair_tool") + and self._engine_running == false then + if self.hp_max < self._max_plane_hp then + local inventory_item = "default:steel_ingot" + local inv = puncher:get_inventory() + if inv:contains_item("main", inventory_item) then + local stack = ItemStack(inventory_item .. " 1") + inv:remove_item("main", stack) + self.hp_max = self.hp_max + 10 + if self.hp_max > self._max_plane_hp then self.hp_max = self._max_plane_hp end + airutils.setText(self, self._vehicle_name) + else + core.chat_send_player(puncher:get_player_name(), S("You need steel ingots in your inventory to perform this repair.")) + end + end + return + end + + -- deal with painting or destroying + if itmstck then + if airutils.set_param_paint(self, puncher, itmstck, 1) == false then + if not self.driver and toolcaps and toolcaps.damage_groups + and toolcaps.groupcaps and (toolcaps.groupcaps.choppy or toolcaps.groupcaps.axey_dig) and item_name ~= airutils.fuel then + --airutils.hurt(self,toolcaps.damage_groups.fleshy - 1) + --airutils.make_sound(self,'hit') + damage_vehicle(self, toolcaps, ttime, damage) + core.sound_play(self._collision_sound, { + object = self.object, + max_hear_distance = 5, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }) + airutils.setText(self, self._vehicle_name) + end + end + end + + if self.hp_max <= 0 then + airutils.destroy(self, name) + end + else + if self._custom_punch_when_attached then self._custom_punch_when_attached(self, puncher) end + end +end + +--returns the vehicle to inventory if it is registered as a tool +local function get_vehicle(self, player) + if not player then return false end + + local itmstck=player:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + --remove + if (item_name == "airutils:repair_tool") and self._engine_running == false then + + local lua_ent = self.object:get_luaentity() + local staticdata = lua_ent:get_staticdata(self) + local obj_name = lua_ent.name + local pos = self.object:get_pos() + + local stack = ItemStack(obj_name) + local tool = false + if stack:get_stack_max() == 1 then tool = true end + + if tool == false then return false end + + local stack_meta = stack:get_meta() + stack_meta:set_string("staticdata", staticdata) + + local inv = player:get_inventory() + if inv then + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + else + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) + end + else + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) + end + + airutils.seats_destroy(self) + local obj_children = self.object:get_children() + for _, child in ipairs(obj_children) do + child:remove() + end + airutils.destroy_inventory(self) + self.object:remove() + + return true + end + + return false +end + +function airutils.on_rightclick(self, clicker) + local message = "" + if not clicker or not clicker:is_player() then + return + end + + local name = clicker:get_player_name() + + if self.owner == "" then + self.owner = name + end + + local copilot_name = nil + if self.co_pilot and self._have_copilot then + copilot_name = self.co_pilot + end + + --core.chat_send_all(dump(self.driver_name)) + + local is_under_water = airutils.check_is_under_water(self.object) + + --core.chat_send_all('name '.. dump(name) .. ' - pilot: ' .. dump(self.driver_name) .. ' - pax: ' .. dump(copilot_name)) + --========================= + -- form to pilot + --========================= + local is_attached = false + local seat = clicker:get_attach() + if seat then + local plane = seat:get_attach() + if plane == self.object then is_attached = true end + end + + if name == self.driver_name then + if is_attached then + local itmstck=clicker:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + --adf program function + if (item_name == "compassgps:cgpsmap_marked") then + local meta = core.deserialize(itmstck:get_metadata()) + if meta then + self._adf_destiny = {x=meta["x"], z=meta["z"]} + end + else + --formspec of the plane + if not self._custom_pilot_formspec then + airutils.pilot_formspec(name) + else + self._custom_pilot_formspec(name) + end + airutils.sit(clicker) + end + else + self.driver_name = nil --error, so clean it + end + --========================= + -- detach copilot + --========================= + elseif name == copilot_name then + if self._command_is_given then + --formspec of the plane + if not self._custom_pilot_formspec then + airutils.pilot_formspec(name) + else + self._custom_pilot_formspec(name) + end + else + airutils.pax_formspec(name) + end + + --========================= + -- attach pilot + --========================= + elseif not self.driver_name and not self._autoflymode then + if self.owner == name or core.check_player_privs(clicker, {protection_bypass=true}) then + + local itmstck=clicker:get_wielded_item() + + if itmstck then + if airutils.set_param_paint(self, clicker, itmstck, 2) == true then + return + end + if get_vehicle(self, clicker) then + return + end + end + + if clicker:get_player_control().sneak == true then --lets see the inventory + airutils.show_vehicle_trunk_formspec(self, clicker, self._trunk_slots) + else + if is_under_water then return end + + --remove the passengers first + local max_seats = table.getn(self._seats) + for i = max_seats,1,-1 + do + if self._passengers[i] and self._passengers[i] ~= "" then + local passenger = core.get_player_by_name(self._passengers[i]) + if passenger then airutils.dettach_pax(self, passenger) end + end + end + + --attach player + --airutils.seat_create(self, 1) + --airutils.seat_create(self, 2) + if clicker:get_player_control().aux1 == true and max_seats > 1 then + -- flight instructor mode + self._instruction_mode = true + self.co_pilot_seat_base = self._passengers_base[1] + self.pilot_seat_base = self._passengers_base[2] + else + -- no driver => clicker is new driver + self._instruction_mode = false + self.co_pilot_seat_base = self._passengers_base[2] + self.pilot_seat_base = self._passengers_base[1] + end + airutils.attach(self, clicker) + self._command_is_given = false + end + else + airutils.dettach_pax(self, clicker) + core.chat_send_player(name, core.colorize('#ff0000', S(" >>> You aren't the owner of this "..self.infotext.."."))) + end + + --========================= + -- attach passenger + --========================= + elseif self.driver_name ~= nil or self._autoflymode == true then + local d_name = self.driver_name + if d_name == nil then d_name = "" end + local player = core.get_player_by_name(d_name) + if player or self._autoflymode == true then + is_attached = airutils.check_passenger_is_attached(self, name) + + if is_attached then + --remove pax + airutils.pax_formspec(name) + else + --attach normal passenger + airutils.attach_pax(self, clicker) + end + + else + core.chat_send_player(clicker:get_player_name(), message) + end + else + core.chat_send_player(clicker:get_player_name(), message) + end + +end + diff --git a/mods/airutils/lib_planes/forms.lua b/mods/airutils/lib_planes/forms.lua new file mode 100644 index 00000000..6f255b94 --- /dev/null +++ b/mods/airutils/lib_planes/forms.lua @@ -0,0 +1,450 @@ +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") +local S = airutils.S +-------------- +-- Manual -- +-------------- + +function airutils.getPlaneFromPlayer(player) + local seat = player:get_attach() + if seat then + local plane = seat:get_attach() + return plane + end + return nil +end + +function airutils.pilot_formspec(name) + local player = minetest.get_player_by_name(name) + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + return + end + local ent = plane_obj:get_luaentity() + + local flap_is_down = "false" + local have_flaps = false + if ent._wing_angle_extra_flaps then + if ent._wing_angle_extra_flaps > 0 then + have_flaps = true + end + end + if have_flaps then + if ent._flap then flap_is_down = "true" end + end + + local light = "false" + if ent._have_landing_lights then + if ent._land_light then light = "true" end + end + + local autopilot = "false" + if ent._have_auto_pilot then + if ent._autopilot then autopilot = "true" end + end + + local yaw = "false" + if ent._yaw_by_mouse then yaw = "true" end + + local eng_status = "false" + local eng_status_color = "#ff0000" + if ent._engine_running then + eng_status = "true" + eng_status_color = "#00ff00" + end + + local ver_pos = 1.0 + local basic_form = "" + --basic_form = basic_form.."button[1,"..ver_pos..";4,1;turn_on;Start/Stop Engines]" + basic_form = basic_form.."checkbox[1,"..ver_pos..";turn_on;"..core.colorize(eng_status_color, S("Start/Stop Engines"))..";"..eng_status.."]" + ver_pos = ver_pos + 1.1 + basic_form = basic_form.."button[1,"..ver_pos..";4,1;hud;" .. S("Show/Hide Gauges") .. "]" + ver_pos = ver_pos + 1.1 + basic_form = basic_form.."button[1,"..ver_pos..";4,1;inventory;" .. S("Show Inventory") .. "]" + ver_pos = ver_pos + 1.5 + + basic_form = basic_form.."checkbox[1,"..ver_pos..";yaw;" .. S("Yaw by mouse") .. ";"..yaw.."]" + ver_pos = ver_pos + 0.5 + + basic_form = basic_form.."button[1,"..ver_pos..";4,1;go_out;" .. S("Go Out!") .. "]" + + --form second part + local expand_form = false + ver_pos = 1.2 --restart in second collumn + if have_flaps then + basic_form = basic_form.."checkbox[6,"..ver_pos..";flap_is_down;" .. S("Flaps down") .. ";"..flap_is_down.."]" + ver_pos = ver_pos + 0.5 + expand_form = true + end + + if ent._have_landing_lights then + basic_form = basic_form.."checkbox[6,"..ver_pos..";light;" .. S("Landing Light") .. ";"..light.."]" + ver_pos = ver_pos + 0.5 + expand_form = true + end + + if ent._have_auto_pilot then + basic_form = basic_form.."checkbox[6,"..ver_pos..";turn_auto_pilot_on;" .. S("Autopilot") .. ";"..autopilot.."]" + ver_pos = ver_pos + 0.5 + expand_form = true + end + + if ent._have_copilot and name == ent.driver_name then + basic_form = basic_form.."button[6,"..ver_pos..";4,1;copilot_form;" .. S("Co-pilot Manager") .. "]" + ver_pos = ver_pos + 1.25 + expand_form = true + end + + if ent._have_adf then + basic_form = basic_form.."button[6,"..ver_pos..";4,1;adf_form;" .. S("Adf Manager") .. "]" + ver_pos = ver_pos + 1.1 + expand_form = true + end + + if ent._have_manual then + basic_form = basic_form.."button[6,5.2;4,1;manual;" .. S("Manual") .. "]" + expand_form = true + end + + local form_width = 6 + if expand_form then form_width = 11 end + local form = table.concat({ + "formspec_version[3]", + "size["..form_width..",7.2]", + }, "") + + minetest.show_formspec(name, "lib_planes:pilot_main", form..basic_form) +end + +function airutils.manage_copilot_formspec(name) + local player = minetest.get_player_by_name(name) + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + return + end + local ent = plane_obj:get_luaentity() + + local pass_list = "" + for k, v in pairs(ent._passengers) do + pass_list = pass_list .. v .. "," + end + + local basic_form = table.concat({ + "formspec_version[3]", + "size[6,4.5]", + }, "") + + basic_form = basic_form.."label[1,1.0;" .. S("Bring a copilot") .. ":]" + + local max_seats = table.getn(ent._seats) + if ent._have_copilot and max_seats > 2 then --no need to select if there are only 2 occupants + basic_form = basic_form.."dropdown[1,1.5;4,0.6;copilot;"..pass_list..";0;false]" + end + + basic_form = basic_form.."button[1,2.5;4,1;pass_control;" .. S("Pass the Control") .. "]" + + minetest.show_formspec(name, "lib_planes:manage_copilot", basic_form) +end + +function airutils.adf_formspec(name) + local player = minetest.get_player_by_name(name) + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + return + end + local ent = plane_obj:get_luaentity() + + local adf = "false" + if ent._adf then adf = "true" end + local x = 0 + local z = 0 + if ent._adf_destiny then + if ent._adf_destiny.x then + if type(ent._adf_destiny.x) ~= nil then + x = math.floor(ent._adf_destiny.x) + end + end + if ent._adf_destiny.z then + if type(ent._adf_destiny.z) ~= nil then + z = math.floor(ent._adf_destiny.z) + end + end + end + + local basic_form = table.concat({ + "formspec_version[3]", + "size[6,3.5]", + }, "") + + basic_form = basic_form.."checkbox[1.0,1.0;adf;" .. S("Auto Direction Find") .. ";"..adf.."]" + basic_form = basic_form.."field[1.0,1.7;1.5,0.6;adf_x;pos x;"..x.."]" + basic_form = basic_form.."field[2.8,1.7;1.5,0.6;adf_z;pos z;"..z.."]" + basic_form = basic_form.."button[4.5,1.7;0.6,0.6;save_adf;" .. S("OK") .. "]" + + minetest.show_formspec(name, "lib_planes:adf_main", basic_form) +end + +function airutils.pax_formspec(name) + local basic_form = table.concat({ + "formspec_version[3]", + "size[6,5]", + }, "") + + basic_form = basic_form.."button[1,1.0;4,1;new_seat;" .. S("Change Seat") .. "]" + basic_form = basic_form.."button[1,2.5;4,1;go_out;" .. S("Go Offboard") .. "]" + + minetest.show_formspec(name, "lib_planes:passenger_main", basic_form) +end + +function airutils.go_out_confirmation_formspec(name) + local basic_form = table.concat({ + "formspec_version[3]", + "size[7,2.2]", + }, "") + + basic_form = basic_form.."label[0.5,0.5;" .. S("Do you really want to go offboard now?") .. "]" + basic_form = basic_form.."button[1.3,1.0;2,0.8;no;" .. S("No") .. "]" + basic_form = basic_form.."button[3.6,1.0;2,0.8;yes;" .. S("Yes") .. "]" + + minetest.show_formspec(name, "lib_planes:go_out_confirmation_form", basic_form) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "lib_planes:go_out_confirmation_form" then + local name = player:get_player_name() + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "lib_planes:go_out_confirmation_form") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.yes then + airutils.dettach_pax(ent, player, true) + end + end + minetest.close_formspec(name, "lib_planes:go_out_confirmation_form") + end + if formname == "lib_planes:adf_main" then + local name = player:get_player_name() + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.chat_send_player(name, core.colorize('#ff0000', S(" >>> There is something wrong with the plane..."))) + minetest.close_formspec(name, "lib_planes:adf_main") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.adf then + if ent._adf == true then + ent._adf = false + minetest.chat_send_player(name, core.colorize('#0000ff', S(" >>> ADF deactivated."))) + else + ent._adf = true + minetest.chat_send_player(name, core.colorize('#00ff00', S(" >>> ADF activated."))) + end + end + if fields.save_adf then + if not ent._adf_destiny then ent._adf_destiny = {x=0,z=0} end + if ent._adf_destiny then + if fields.adf_x and fields.adf_z then + if tonumber(fields.adf_x, 10) ~= nil and tonumber(fields.adf_z, 10) ~= nil then + ent._adf_destiny.x = tonumber(fields.adf_x, 10) + ent._adf_destiny.z = tonumber(fields.adf_z, 10) + minetest.chat_send_player(name, core.colorize('#00ff00', S(" >>> Destination written successfully."))) + else + minetest.chat_send_player(name, core.colorize('#ff0000', S(" >>> There is something wrong with the ADF fields values."))) + end + else + minetest.chat_send_player(name, core.colorize('#ff0000', S(" >>> Both ADF fields must be given to complete the operation."))) + end + end + end + else + minetest.chat_send_player(name, core.colorize('#ff0000', S(" >>> There is something wrong on ADF saving..."))) + end + minetest.close_formspec(name, "lib_planes:adf_main") + end + if formname == "lib_planes:passenger_main" then + local name = player:get_player_name() + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "lib_planes:passenger_main") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.new_seat then + airutils.dettach_pax(ent, player) + airutils.attach_pax(ent, player) + end + if fields.go_out then + local touching_ground, _ = airutils.check_node_below(plane_obj, 2.5) + if ent.isinliquid or touching_ground then --isn't flying? + airutils.dettach_pax(ent, player) + else + airutils.go_out_confirmation_formspec(name) + end + end + end + minetest.close_formspec(name, "lib_planes:passenger_main") + end + if formname == "lib_planes:pilot_main" then + local name = player:get_player_name() + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj then + local ent = plane_obj:get_luaentity() + if fields.turn_on then + airutils.start_engine(ent) + end + if fields.hud then + if ent._show_hud == true then + ent._show_hud = false + else + ent._show_hud = true + end + end + if fields.go_out then + local touch_point = ent.initial_properties.collisionbox[2]-1.0 + -----//// + local pos = plane_obj:get_pos() + pos.y = pos.y + touch_point + local node_below = minetest.get_node(pos).name + local nodedef = minetest.registered_nodes[node_below] + local is_on_ground = not nodedef or nodedef.walkable or false -- unknown nodes are solid + + if ent.driver_name == name and ent.owner == ent.driver_name then --just the owner can do this + --minetest.chat_send_all(dump(noded)) + if is_on_ground then --or clicker:get_player_control().sneak then + --minetest.chat_send_all(dump("is on ground")) + --remove the passengers first + local max_seats = table.getn(ent._seats) + for i = max_seats,1,-1 + do + --minetest.chat_send_all("index: "..i.." - "..dump(ent._passengers[i])) + if ent._passengers[i] then + local passenger = minetest.get_player_by_name(ent._passengers[i]) + if passenger then airutils.dettach_pax(ent, passenger) end + end + end + ent._instruction_mode = false + else + -- not on ground + if ent.co_pilot then + --give the control to the pax + ent._autopilot = false + airutils.transfer_control(ent, true) + ent._command_is_given = true + ent._instruction_mode = true + end + end + end + airutils.dettach_pax(ent, player) + end + if fields.inventory then + if ent._trunk_slots then + airutils.show_vehicle_trunk_formspec(ent, player, ent._trunk_slots) + end + end + if fields.flap_is_down then + if fields.flap_is_down == "true" then + ent._flap = true + else + ent._flap = false + end + minetest.sound_play("airutils_collision", { + object = ent.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 0.5, + }, true) + end + if fields.light then + if ent._land_light == true then + ent._land_light = false + else + ent._land_light = true + end + end + if fields.yaw then + if ent._yaw_by_mouse == true then + ent._yaw_by_mouse = false + else + ent._yaw_by_mouse = true + end + end + if fields.copilot_form then + airutils.manage_copilot_formspec(name) + end + if fields.adf_form then + airutils.adf_formspec(name) + end + if fields.turn_auto_pilot_on then + if ent._autopilot == true then + ent._autopilot = false + core.chat_send_player(ent.driver_name,S(" >>> Autopilot deactivated")) + else + ent._autopilot = true + core.chat_send_player(ent.driver_name,core.colorize('#00ff00', S(" >>> Autopilot activated"))) + end + end + if fields.manual then + if ent._have_manual then + ent._have_manual(name) + end + end + end + minetest.close_formspec(name, "lib_planes:pilot_main") + end + if formname == "lib_planes:manage_copilot" then + local name = player:get_player_name() + local plane_obj = airutils.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "lib_planes:manage_copilot") + return + end + local ent = plane_obj:get_luaentity() + + if fields.copilot then + --look for a free seat first + local is_there_a_free_seat = false + for i = 2,1,-1 + do + if ent._passengers[i] == nil then + is_there_a_free_seat = true + break + end + end + --then move the current copilot to a free seat + if ent.co_pilot and is_there_a_free_seat then + local copilot_player_obj = minetest.get_player_by_name(ent.co_pilot) + if copilot_player_obj then + airutils.dettach_pax(ent, copilot_player_obj) + airutils.attach_pax(ent, copilot_player_obj) + else + ent.co_pilot = nil + end + end + --so bring the new copilot + if ent.co_pilot == nil then + local new_copilot_player_obj = minetest.get_player_by_name(fields.copilot) + if new_copilot_player_obj then + airutils.dettach_pax(ent, new_copilot_player_obj) + airutils.attach_pax(ent, new_copilot_player_obj, true) + end + end + end + if fields.pass_control then + if ent._command_is_given == true then + --take the control + airutils.transfer_control(ent, false) + else + --trasnfer the control to student + airutils.transfer_control(ent, true) + end + end + minetest.close_formspec(name, "lib_planes:manage_copilot") + end + + +end) diff --git a/mods/airutils/lib_planes/fuel_management.lua b/mods/airutils/lib_planes/fuel_management.lua new file mode 100644 index 00000000..5b6225d7 --- /dev/null +++ b/mods/airutils/lib_planes/fuel_management.lua @@ -0,0 +1,70 @@ +function airutils.contains(table, val) + for k,v in pairs(table) do + if k == val then + return v + end + end + return false +end + +function airutils.loadFuel(self, player_name) + local player = minetest.get_player_by_name(player_name) + local inv = player:get_inventory() + local itmstck=player:get_wielded_item() + + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + local fuel = airutils.contains(airutils.fuel, item_name) + if fuel then + --local stack = ItemStack(item_name .. " 1") + + if self._energy < self._max_fuel then + itmstck:set_count(1) + inv:remove_item("main", itmstck) + self._energy = self._energy + fuel + if self._energy > self._max_fuel then self._energy = self._max_fuel end + + --local energy_indicator_angle = airutils.get_gauge_angle(self._energy) + --self.fuel_gauge:set_attach(self.object,'',self._gauge_fuel_position,{x=0,y=0,z=energy_indicator_angle}) + end + + return true + end + + return false +end + +function airutils.consumptionCalc(self, accel) + if accel == nil then return end + if self._energy > 0 and self._engine_running and accel ~= nil then + local divisor = 700000 + if self._fuel_consumption_divisor then divisor = self._fuel_consumption_divisor end + local consumed_power = 0 + local parent_obj = self.object:get_attach() + if not parent_obj then + if self._rotor_speed then + --is an helicopter + consumed_power = 50/divisor --fixed rpm + else + --is a normal plane + consumed_power = self._power_lever/divisor + end + end + --minetest.chat_send_all('consumed: '.. consumed_power) + self._energy = self._energy - consumed_power; + + local energy_indicator_angle = airutils.get_gauge_angle(self._energy) + if self.fuel_gauge then + if self.fuel_gauge:get_luaentity() then + self.fuel_gauge:set_attach(self.object,'',self._gauge_fuel_position,{x=0,y=0,z=energy_indicator_angle}) + end + end + end + if self._energy <= 0 and self._engine_running and accel ~= nil then + self._engine_running = false + self._autopilot = false + if self.sound_handle then minetest.sound_stop(self.sound_handle) end + self.object:set_animation_frame_speed(0) + end +end diff --git a/mods/airutils/lib_planes/gauges.lua b/mods/airutils/lib_planes/gauges.lua new file mode 100644 index 00000000..28e83704 --- /dev/null +++ b/mods/airutils/lib_planes/gauges.lua @@ -0,0 +1,134 @@ +--[[ +local function get_pointer(pointer_angle, gauge_center_x, gauge_center_y, full_pointer) + full_pointer = full_pointer or 1 + local retval = "" + local ind_pixel = "airutils_ind_box_2.png" + + local pointer_img_size = 8 + local pointer_rad = math.rad(pointer_angle) + local dim = 2*(pointer_img_size/2) + local pos_x = math.sin(pointer_rad) * dim + local pos_y = math.cos(pointer_rad) * dim + retval = retval..(gauge_center_x+pos_x)..","..(gauge_center_y+pos_y).."="..ind_pixel..":" + + dim = 4*(pointer_img_size/2) + pos_x = math.sin(pointer_rad) * dim + pos_y = math.cos(pointer_rad) * dim + retval = retval..(gauge_center_x+pos_x)..","..(gauge_center_y+pos_y).."="..ind_pixel..":" + + dim = 6*(pointer_img_size/2) + pos_x = math.sin(pointer_rad) * dim + pos_y = math.cos(pointer_rad) * dim + retval = retval..(gauge_center_x+pos_x)..","..(gauge_center_y+pos_y).."="..ind_pixel..":" + + if full_pointer == 1 then + dim = 8*(pointer_img_size/2) + pos_x = math.sin(pointer_rad) * dim + pos_y = math.cos(pointer_rad) * dim + retval = retval..(gauge_center_x+pos_x)..","..(gauge_center_y+pos_y).."="..ind_pixel..":" + + dim = 10*(pointer_img_size/2) + pos_x = math.sin(pointer_rad) * dim + pos_y = math.cos(pointer_rad) * dim + retval = retval..(gauge_center_x+pos_x)..","..(gauge_center_y+pos_y).."="..ind_pixel..":" + end + return retval +end +]]-- + +function airutils.plot_altimeter_gauge(self, scale, place_x, place_y) + local bg_width_height = 100 + local pointer_img = 8 + local gauge_center = (bg_width_height / 2) - (pointer_img/2) + local gauge_center_x = place_x + gauge_center + local gauge_center_y = place_y + gauge_center + + + --altimeter + --[[local altitude = (height / 0.32) / 100 + local hour, minutes = math.modf( altitude ) + hour = math.fmod (hour, 10) + minutes = minutes * 100 + minutes = (minutes * 100) / 100 + local minute_angle = (minutes*-360)/100 + local hour_angle = (hour*-360)/10 + ((minute_angle*36)/360)]]-- + + --[[ + #### `[combine:x:,=:,=:...` + + * ``: width + * ``: height + * ``: x position + * ``: y position + * ``: texture to combine + + Creates a texture of size `` times `` and blits the listed files to their + specified coordinates. + + ]]-- + + local altimeter = "^[resize:"..scale.."x"..scale.."^[combine:"..bg_width_height.."x"..bg_width_height..":" + ..place_x..","..place_y.."=airutils_altimeter_gauge.png:" + + --altimeter = altimeter..get_pointer(minute_angle+180, gauge_center_x, gauge_center_y, 1) + --altimeter = altimeter..get_pointer(hour_angle+180, gauge_center_x, gauge_center_y, 0) + + return altimeter +end + +function airutils.plot_fuel_gauge(self, scale, place_x, place_y) + local bg_width_height = 100 + local pointer_img = 8 + local gauge_center = (bg_width_height / 2) - (pointer_img/2) + local gauge_center_x = place_x + gauge_center + local gauge_center_y = place_y + gauge_center + + --local fuel_percentage = (curr_level*100)/max_level + --local fuel_angle = -(fuel_percentage*180)/100 + --minetest.chat_send_all(dump(fuel_angle)) + + local fuel = "^[resize:"..scale.."x"..scale.."^[combine:"..bg_width_height.."x"..bg_width_height..":" + ..place_x..","..place_y.."=airutils_fuel_gauge.png:" + + --fuel = fuel..get_pointer(fuel_angle-90, gauge_center_x, gauge_center_y, 1) + + return fuel +end + +function airutils.plot_speed_gauge(self, scale, place_x, place_y) + local bg_width_height = 100 + local pointer_img = 8 + local gauge_center = (bg_width_height / 2) - (pointer_img/2) + local gauge_center_x = place_x + gauge_center + local gauge_center_y = place_y + gauge_center + + --local speed_percentage = (curr_level*100)/max_level + --local speed_angle = -(speed_percentage*350)/100 + --minetest.chat_send_all(dump(fuel_angle)) + + local speed = "^[resize:"..scale.."x"..scale.."^[combine:"..bg_width_height.."x"..bg_width_height..":" + ..place_x..","..place_y.."=airutils_speed_gauge.png:" + + --fuel = fuel..get_pointer(speed_angle-180, gauge_center_x, gauge_center_y, 1) + + return speed +end + +function airutils.plot_power_gauge(self, scale, place_x, place_y) + local bg_width_height = 100 + local pointer_img = 8 + local gauge_center = (bg_width_height / 2) - (pointer_img/2) + local gauge_center_x = place_x + gauge_center + local gauge_center_y = place_y + gauge_center + + --local speed_percentage = (curr_level*100)/max_level + --local speed_angle = -(speed_percentage*350)/100 + --minetest.chat_send_all(dump(fuel_angle)) + + local rpm = "^[resize:"..scale.."x"..scale.."^[combine:"..bg_width_height.."x"..bg_width_height..":" + ..place_x..","..place_y.."=airutils_rpm_gauge.png:" + + --fuel = fuel..get_pointer(speed_angle-180, gauge_center_x, gauge_center_y, 1) + + return rpm +end diff --git a/mods/airutils/lib_planes/global_definitions.lua b/mods/airutils/lib_planes/global_definitions.lua new file mode 100644 index 00000000..3f14b814 --- /dev/null +++ b/mods/airutils/lib_planes/global_definitions.lua @@ -0,0 +1,8 @@ +-- +-- constants +-- +airutils.vector_up = vector.new(0, 1, 0) + +--set min y-pos above which airplanes are seen on radar +airutils.radarMinHeight = 30 + diff --git a/mods/airutils/lib_planes/hud.lua b/mods/airutils/lib_planes/hud.lua new file mode 100644 index 00000000..a06b1437 --- /dev/null +++ b/mods/airutils/lib_planes/hud.lua @@ -0,0 +1,328 @@ +airutils.hud_list = {} +local S = airutils.S + +function airutils.animate_gauge(player, ids, prefix, x, y, angle) + local angle_in_rad = math.rad(angle + 180) + local dim = 10 + local pos_x = math.sin(angle_in_rad) * dim + local pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "2"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 20 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "3"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 30 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "4"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 40 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "5"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 50 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "6"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 60 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "7"], "offset", {x = pos_x + x, y = pos_y + y}) +end + +function airutils.update_hud(player, climb, speed, power, fuel) + local player_name = player:get_player_name() + + local screen_pos_y = -150 + local screen_pos_x = 10 + + local clb_gauge_x = screen_pos_x + 75 + local clb_gauge_y = screen_pos_y + 1 + local sp_gauge_x = screen_pos_x + 170 + local sp_gauge_y = clb_gauge_y + + local pwr_gauge_x = screen_pos_x + 330 + local pwr_gauge_y = clb_gauge_y + + local fu_gauge_x = screen_pos_x + 340 + local fu_gauge_y = clb_gauge_y + + local ids = airutils.hud_list[player_name] + if ids then + airutils.animate_gauge(player, ids, "clb_pt_", clb_gauge_x, clb_gauge_y, climb) + airutils.animate_gauge(player, ids, "sp_pt_", sp_gauge_x, sp_gauge_y, speed) + airutils.animate_gauge(player, ids, "pwr_pt_", pwr_gauge_x, pwr_gauge_y, power) + airutils.animate_gauge(player, ids, "fu_pt_", fu_gauge_x, fu_gauge_y, fuel) + else + ids = {} + + ids["title"] = player:hud_add({ + hud_elem_type = "text", + position = {x = 0, y = 1}, + offset = {x = screen_pos_x +140, y = screen_pos_y -100}, + text = S("Flight Information"), + alignment = 0, + scale = { x = 100, y = 30}, + number = 0xFFFFFF, + }) + + ids["bg"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = screen_pos_x, y = screen_pos_y}, + text = "airutils_hud_panel.png", + scale = { x = 0.5, y = 0.5}, + alignment = { x = 1, y = 0 }, + }) + + ids["clb_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["clb_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["clb_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = clb_gauge_x, y = clb_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["sp_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["sp_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = sp_gauge_x, y = sp_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["pwr_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["pwr_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["pwr_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = pwr_gauge_x, y = pwr_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["fu_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["fu_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["fu_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = fu_gauge_x, y = fu_gauge_y}, + text = "airutils_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + airutils.hud_list[player_name] = ids + end +end + + +function airutils.remove_hud(player) + if player then + local player_name = player:get_player_name() + --minetest.chat_send_all(player_name) + local ids = airutils.hud_list[player_name] + if ids then + --player:hud_remove(ids["altitude"]) + --player:hud_remove(ids["time"]) + for key in pairs(ids) do + player:hud_remove(ids[key]) + end + end + airutils.hud_list[player_name] = nil + end + +end diff --git a/mods/airutils/lib_planes/init.lua b/mods/airutils/lib_planes/init.lua new file mode 100644 index 00000000..e7ec634b --- /dev/null +++ b/mods/airutils/lib_planes/init.lua @@ -0,0 +1,19 @@ + + +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "control.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "fuel_management.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "custom_physics.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "utilities.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "entities.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "forms.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "gauges.lua") + +-- +-- helpers and co. +-- + + +-- +-- items +-- diff --git a/mods/airutils/lib_planes/utilities.lua b/mods/airutils/lib_planes/utilities.lua new file mode 100644 index 00000000..e4da815f --- /dev/null +++ b/mods/airutils/lib_planes/utilities.lua @@ -0,0 +1,1406 @@ +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "global_definitions.lua") +dofile(minetest.get_modpath("airutils") .. DIR_DELIM .. "lib_planes" .. DIR_DELIM .. "hud.lua") + +local S = airutils.S + +function airutils.properties_copy(origin_table) + local tablecopy = {} + for k, v in pairs(origin_table) do + tablecopy[k] = v + end + return tablecopy +end + +function airutils.get_hipotenuse_value(point1, point2) + return math.sqrt((point1.x - point2.x) ^ 2 + (point1.y - point2.y) ^ 2 + (point1.z - point2.z) ^ 2) +end + +function airutils.dot(v1,v2) + return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z +end + +function airutils.sign(n) + return n>=0 and 1 or -1 +end + +function airutils.minmax(v,m) + return math.min(math.abs(v),m)*airutils.sign(v) +end + +function airutils.get_gauge_angle(value, initial_angle) + initial_angle = initial_angle or 90 + local angle = value * 18 + angle = angle - initial_angle + angle = angle * -1 + return angle +end + +local function sit_player(player, name) + if not player then return end + if airutils.is_minetest then + player_api.player_attached[name] = true + player_api.set_animation(player, "sit") + elseif airutils.is_mcl then + mcl_player.player_attached[name] = true + mcl_player.player_set_animation(player, "sit" , 30) + airutils.sit(player) + end + + -- make the driver sit + minetest.after(1, function() + if player then + --minetest.chat_send_all("okay") + airutils.sit(player) + --apply_physics_override(player, {speed=0,gravity=0,jump=0}) + end + end) +end + +-- attach player +function airutils.attach(self, player, instructor_mode) + if not player then return end + if self._needed_licence then + local can_fly = minetest.check_player_privs(player, self._needed_licence) + if not can_fly then + minetest.chat_send_player(player:get_player_name(), core.colorize('#ff0000', S(' >>> You need the priv') .. '"'..self._needed_licence..'" ' .. S('to fly this plane.'))) + return + end + end + + instructor_mode = instructor_mode or false + local name = player:get_player_name() + self.driver_name = name + + -- attach the driver + local eye_y = 0 + if instructor_mode == true and self._have_copilot and self._passengers[2] == "" then + eye_y = -4 + --airutils.seat_create(self, 1) + --airutils.seat_create(self, 2) + + if not self.co_pilot_seat_base then + self.co_pilot_seat_base = self._passengers_base[2] + end + player:set_attach(self.co_pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + self._passengers[2] = name + else + eye_y = -4 + --airutils.seat_create(self, 1) + if not self.pilot_seat_base then + self.pilot_seat_base = self._passengers_base[1] + end + player:set_attach(self.pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + self._passengers[1] = name + end + + if airutils.detect_player_api(player) == 1 then + eye_y = eye_y + 6.5 + end + if airutils.detect_player_api(player) == 2 then + eye_y = -4 + end + + player:set_eye_offset({x = 0, y = eye_y, z = 2}, {x = 0, y = 1, z = -30}) + sit_player(player, name) +end + +local function do_attach(self, player, slot) + if slot == 0 then return end + if self._passengers[slot] == "" then + local name = player:get_player_name() + --minetest.chat_send_all(self.driver_name) + self._passengers[slot] = name + --airutils.seat_create(self, slot) + player:set_attach(self._passengers_base[slot], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + + local eye_y = -4 + if airutils.detect_player_api(player) == 1 then + eye_y = 2.5 + end + player:set_eye_offset({x = 0, y = eye_y, z = 2}, {x = 0, y = 3, z = -30}) + sit_player(player, name) + end +end + +function airutils.dettachPlayer(self, player) + local name = self.driver_name + airutils.setText(self, self._vehicle_name) + + airutils.remove_hud(player) + + --self._engine_running = false + + --check for external attachment of the vehicle + local extern_attach = self.object:get_attach() + local extern_ent = nil + if extern_attach then + extern_ent = extern_attach:get_luaentity() + end + + -- driver clicked the object => driver gets off the vehicle + self.driver_name = nil + + -- detach the player + --player:set_physics_override({speed = 1, jump = 1, gravity = 1, sneak = true}) + if player then + player:set_detach() + player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) + if airutils.is_minetest then + if player_api.player_attached[name] then + player_api.player_attached[name] = nil + end + player_api.set_animation(player, "stand") + elseif airutils.is_mcl then + if mcl_player.player_attached[name] then + mcl_player.player_attached[name] = nil + end + mcl_player.player_set_animation(player, "stand") + end + end + self.driver = nil + --remove_physics_override(player, {speed=1,gravity=1,jump=1}) + + --move the player to the parent ship if any + if extern_ent then + if extern_ent.on_rightclick then + extern_ent.on_rightclick(extern_ent, player) + end + end +end + +function airutils.check_passenger_is_attached(self, name) + local is_attached = false + if self._passenger == name then is_attached = true end + if is_attached == false then + local max_occupants = table.getn(self._seats) + for i = max_occupants,1,-1 + do + if self._passengers[i] == name then + is_attached = true + break + end + end + end + return is_attached +end + +local function attach_copilot(self, name, player, eye_y) + --airutils.seat_create(self, 2) + if not self.co_pilot_seat_base or not player then return end + self.co_pilot = name + self._passengers[2] = name + -- attach the driver + player:set_attach(self.co_pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + player:set_eye_offset({x = 0, y = eye_y, z = 2}, {x = 0, y = 3, z = -30}) + + sit_player(player, name) +end + +-- attach passenger +function airutils.attach_pax(self, player, is_copilot) + if not player then return end + local is_copilot = is_copilot or false + local name = player:get_player_name() + + local eye_y = -4 + if airutils.detect_player_api(player) == 1 then + eye_y = 2.5 + end + + if is_copilot == true then + if self.co_pilot == nil then + attach_copilot(self, name, player, eye_y) + end + else + --randomize the seat + local max_seats = table.getn(self._seats) + local crew = 1 + if self._have_copilot and max_seats > 2 then + crew = crew + 1 + else + attach_copilot(self, name, player, eye_y) + return + end + + local t = {} -- new array + for i=1, max_seats - crew do --(the first are for the crew + t[i] = i + end + + for i = 1, #t*2 do + local a = math.random(#t) + local b = math.random(#t) + t[a],t[b] = t[b],t[a] + end + + --for i = 1,10,1 do + local i = 0 + for k,v in ipairs(t) do + i = t[k] + crew --jump the crew seats + if self._passengers[i] and self._passengers[i] == "" then + --minetest.chat_send_all(self.driver_name) + self._passengers[i] = name + --airutils.seat_create(self, i) + player:set_attach(self._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + player:set_eye_offset({x = 0, y = eye_y, z = 0}, {x = 0, y = 3, z = -30}) + + sit_player(player, name) + break + end + end + + end +end + +function airutils.dettach_pax(self, player, is_flying) + if not player then return end + is_flying = is_flying or false + local name = player:get_player_name() --self._passenger + airutils.remove_hud(player) + + --check for external attachment of the vehicle + local extern_attach = self.object:get_attach() + local extern_ent = nil + if extern_attach then + extern_ent = extern_attach:get_luaentity() + end + + -- passenger clicked the object => driver gets off the vehicle + if self.co_pilot == name then + self.co_pilot = nil + self._passengers[2] = "" + else + local max_seats = table.getn(self._seats) + for i = max_seats,1,-1 + do + if self._passengers[i] == name then + self._passengers[i] = "" + break + end + end + end + + -- detach the player + if player then + if name == self.driver_name then + self.driver_name = nil + end + + local pos = player:get_pos() + player:set_detach() + if is_flying then + pos.y = pos.y - self.initial_properties.collisionbox[2] - 2 + player:set_pos(pos) + end + + if airutils.is_minetest then + player_api.player_attached[name] = nil + player_api.set_animation(player, "stand") + elseif airutils.is_mcl then + mcl_player.player_attached[name] = nil + mcl_player.player_set_animation(player, "stand") + end + + player:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) + --remove_physics_override(player, {speed=1,gravity=1,jump=1}) + + --move the player to the parent ship if any + if extern_ent then + if extern_ent.on_rightclick then + extern_ent.on_rightclick(extern_ent, player) + end + end + end +end + +function airutils.checkAttach(self, player) + if player then + local player_attach = player:get_attach() + if player_attach then + local max_seats = table.getn(self._seats) + for i = max_seats,1,-1 + do + if self._passengers_base[i] then + if player_attach == self._passengers_base[i] then + return true + end + end + end + end + end + return false +end + +local function spawn_drops(self, pos) + if self._drops then + for k,v in pairs(self._drops) do + --print(k,v) + for i=1,v do + minetest.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},k) + end + end + end +end + +function airutils.destroy(self, by_name, by_automation) + by_name = by_name or "" + by_automation = by_automation or false + local with_fire = self._enable_fire_explosion + local owner = self.owner + if by_name == owner then with_fire = false end + local pos = self.object:get_pos() + local rot = self.object:get_rotation() + local trunk_slots = self._trunk_slots + local inv_id = self._inv_id + if pos == nil then return end + + if owner and self._vehicle_name then + minetest.log("action", "airutils: The player "..owner.." had it's "..self._vehicle_name.." destroyed at position x="..math.floor(pos.x).." y="..math.floor(pos.y).." z="..math.floor(pos.z)) + else + minetest.log("action", "airutils: An airplane was destroyed at position x="..math.floor(pos.x).." y="..math.floor(pos.y).." z="..math.floor(pos.z)) + end + + if self.sound_handle then + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + end + + --remove the passengers first + local max_seats = table.getn(self._seats) + for i = max_seats,2,-1 + do + if self._passengers[i] and self._passengers[i] ~= "" then + local passenger = minetest.get_player_by_name(self._passengers[i]) + if passenger then airutils.dettach_pax(self, passenger) end + end + end + + if self.driver_name then + -- detach the driver + local player = minetest.get_player_by_name(self.driver_name) + airutils.dettachPlayer(self, player) + end + + if by_automation == false then + airutils.add_destruction_effects(pos, 5, with_fire) + end + + airutils.seats_destroy(self) + if self._destroy_parts_method then + self._destroy_parts_method(self) + end + local obj_children = self.object:get_children() + for _, child in ipairs(obj_children) do + child:remove() + end + + local destroyed_ent = nil + if by_automation == false then + if self._destroyed_ent then + destroyed_ent = self._destroyed_ent + end + + --if dont have a destroyed version, destroy the inventory + if not destroyed_ent then + airutils.destroy_inventory(self) + spawn_drops(self, pos) + else + if not with_fire then --or by the owner itself + airutils.destroy_inventory(self) + spawn_drops(self, pos) + end + end + else + airutils.destroy_inventory(self) + end + + self.object:remove() + + if airutils.blast_damage == true and with_fire == true and by_automation == false then + airutils.add_blast_damage(pos, 7, 10) + if destroyed_ent then + + local dest_ent = minetest.add_entity(pos, destroyed_ent) + if dest_ent then + local ent = dest_ent:get_luaentity() + if ent then + ent.owner = owner + ent._inv_id = inv_id + ent._trunk_slots = trunk_slots + ent._game_time = minetest.get_gametime() + dest_ent:set_yaw(rot.y) + end + end + + end + end +end + +function airutils.testImpact(self, velocity, position) + if self.hp_max < 0 then --if acumulated damage is greater than 50, adieu + airutils.destroy(self) + end + if velocity == nil then return end + local impact_speed = 2 + local p = position --self.object:get_pos() + local collision = false + if self._last_vel == nil then return end + local touch_point = self.initial_properties.collisionbox[2]-0.5 + --lets calculate the vertical speed, to avoid the bug on colliding on floor with hard lag + if math.abs(velocity.y - self._last_vel.y) > impact_speed then + local noded = airutils.nodeatpos(airutils.pos_shift(p,{y=touch_point})) + if (noded and noded.drawtype ~= 'airlike') then + collision = true + else + self.object:set_velocity(self._last_vel) + --self.object:set_acceleration(self._last_accell) + --self.object:set_velocity(vector.add(velocity, vector.multiply(self._last_accell, self.dtime/8))) + end + end + local impact = math.abs(airutils.get_hipotenuse_value(velocity, self._last_vel)) + local vertical_impact = math.abs(velocity.y - self._last_vel.y) + + --minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max) + if impact > impact_speed then + --minetest.chat_send_all('impact: '.. impact .. ' - hp: ' .. self.hp_max) + if self.colinfo then + collision = self.colinfo.collides + end + end + + if self._last_water_touch == nil then self._last_water_touch = 3 end + if self._last_water_touch <= 3 then self._last_water_touch = self._last_water_touch + self.dtime end + if impact > 0.2 and self._longit_speed > 0.6 and self._last_water_touch >=3 then + local noded = airutils.nodeatpos(airutils.pos_shift(p,{y=touch_point})) + if (noded and noded.drawtype ~= 'airlike') then + if noded.drawtype == 'liquid' then + self._last_water_touch = 0 + minetest.sound_play("airutils_touch_water", { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + return + end + end + end + + if self._last_touch == nil then self._last_touch = 1 end + if self._last_touch <= 1 then self._last_touch = self._last_touch + self.dtime end + if vertical_impact > 1.0 and self._longit_speed > self._min_speed/2 and self._last_touch >= 1 then + local noded = airutils.nodeatpos(airutils.pos_shift(p,{y=touch_point})) + if (noded and noded.drawtype ~= 'airlike') and (noded.drawtype ~= 'liquid') then + self._last_touch = 0 + if not self._ground_friction then self._ground_friction = 0.99 end + + if self._ground_friction > 0.97 and self.wheels then + minetest.sound_play("airutils_touch", { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + else + minetest.sound_play("airutils_collision", { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + end + end + end + + --damage by speed + if self._speed_not_exceed then + if self._last_speed_damage_time == nil then self._last_speed_damage_time = 0 end + self._last_speed_damage_time = self._last_speed_damage_time + self.dtime + if self._last_speed_damage_time > 2 then self._last_speed_damage_time = 2 end + if math.abs(self._longit_speed) > self._speed_not_exceed and self._last_speed_damage_time >= 2 then + self._last_speed_damage_time = 0 + minetest.sound_play("airutils_collision", { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + self.hp_max = self.hp_max - self._damage_by_wind_speed + if self.driver_name then + airutils.setText(self, self._vehicle_name) + end + if self.hp_max < 0 then --if acumulated damage is greater than 50, adieu + airutils.destroy(self) + end + end + end + + if collision then + local damage = impact/2 --default for basic planes and trainers + if self._hard_damage then + damage = impact*3 + --check if the impact was on landing gear area + --[[if math.abs(impact - vertical_impact) < (impact*0.1) and --vert speed difference less than 10% of total + math.abs(math.deg(self.object:get_rotation().x)) < 20 and --nose angle between +20 and -20 degrees + self._longit_speed < (self._min_speed*2) then --longit speed less than the double of min speed + self._longit_speed > (self._min_speed/2) then --longit speed bigger than the half of min speed + damage = impact / 2 --if the plane was landing, the damage is mainly on landing gear, so lets reduce the damage + end]]-- + --end check + if math.abs(math.deg(self.object:get_rotation().x)) < 20 and --nose angle between +20 and -20 degrees + self._longit_speed < (self._min_speed*2) then --longit speed less than the double of min speed + damage = impact / 2 --if the plane was landing, the damage is mainly on landing gear, so lets reduce the damage + local new_vel = self.object:get_velocity() + new_vel.y = 0 + self.object:set_velocity(new_vel) --TODO something is causing the plane to explode after a shaking, so I'm reseting the speed until I discover the bug + end + end + + self.hp_max = self.hp_max - damage --subtract the impact value directly to hp meter + minetest.sound_play(self._collision_sound, { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + + --stop engine + if damage > 7 then + self._power_lever = 0 + self._engine_running = false + end + + airutils.setText(self, self._vehicle_name) + + if self.driver_name then + local player_name = self.driver_name + + --minetest.chat_send_all('damage: '.. damage .. ' - hp: ' .. self.hp_max) + if self.hp_max < 0 then --adieu + airutils.destroy(self) + end + + local player = minetest.get_player_by_name(player_name) + if player then + if player:get_hp() > 0 then + local hurt_by_impact_divisor = 0.5 --less is more + if self.hp_max > 0 then hurt_by_impact_divisor = 4 end + player:set_hp(player:get_hp()-(damage/hurt_by_impact_divisor)) + end + end + if self._passenger ~= nil then + local passenger = minetest.get_player_by_name(self._passenger) + if passenger then + if passenger:get_hp() > 0 then + passenger:set_hp(passenger:get_hp()-(damage/2)) + end + end + end + end + + end +end + +--this method checks for a disconected player who comes back +function airutils.rescueConnectionFailedPassengers(self) + if self._disconnection_check_time == nil then self._disconnection_check_time = 1 end + self._disconnection_check_time = self._disconnection_check_time + self.dtime + if not self._passengers_base then return end + local max_seats = table.getn(self._passengers_base) + if self._disconnection_check_time > 1 then + --minetest.chat_send_all(dump(self._passengers)) + self._disconnection_check_time = 0 + for i = max_seats,1,-1 + do + if self._passengers[i] and self._passengers[i] ~= "" then + local player = minetest.get_player_by_name(self._passengers[i]) + if player then --we have a player! + if player:get_attach() == nil then + --if player_api.player_attached[self._passengers[i]] == nil then --but isn't attached? + --minetest.chat_send_all("okay") + if player:get_hp() > 0 then + self._passengers[i] = "" --clear the slot first + do_attach(self, player, i) --attach + end + end + end + end + end + end +end + +function airutils.checkattachBug(self) + -- for some engine error the player can be detached from the submarine, so lets set him attached again + local have_driver = (self.driver_name ~= nil) + if have_driver then + -- attach the driver again + local player = minetest.get_player_by_name(self.driver_name) + if player then + if player:get_hp() > 0 then + if player:get_attach() == nil then + --no attach, lets recover + airutils.attach(self, player, self._instruction_mode) + return + end + else + --the player is dead, lets drop + airutils.dettachPlayer(self, player) + return + end + else + if (self._passenger ~= nil or self.co_pilot ~= nil) and self._command_is_given == false then + --no pilot?! a passenger is the pilot now + self._autopilot = false + airutils.transfer_control(self, true) + return + end + end + end + + --force attach here to prevent desyncronization during fly (it happens in some map areas on my world) + local base_value = 1.0 + if self._seat_check_interval == nil then self._seat_check_interval = base_value end + self._seat_check_interval = self._seat_check_interval + self.dtime + + if self._seat_check_interval >= base_value then + self._seat_check_interval = 0 + local max_seats = table.getn(self._seats) + for i = max_seats,1,-1 + do + if self._passengers[i] and self._passengers[i] ~= "" then + local player = minetest.get_player_by_name(self._passengers[i]) + if player then --we have a player! + --minetest.chat_send_all(dump(i).." >> "..self._passengers[i].." >> "..dump(self._passengers).." >> instruction: "..dump(self._instruction_mode)) + if self._passengers[i] == self.driver_name and self._instruction_mode then + player:set_attach(self.pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + elseif self._passengers[i] == self.co_pilot and self._instruction_mode then + player:set_attach(self.co_pilot_seat_base, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + else + player:set_attach(self._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + end + end + end + end + end +end + +function airutils.engineSoundPlay(self) + --sound + if self.sound_handle then minetest.sound_stop(self.sound_handle) end + if self.object then + local pitch_adjust = 0.5 + ((self._power_lever/100)/2) + self.sound_handle = minetest.sound_play({name = self._engine_sound}, + {object = self.object, gain = 2.0, + pitch = pitch_adjust, + max_hear_distance = 15, + loop = true,}) + end +end + +function airutils.engine_set_sound_and_animation(self) + --minetest.chat_send_all('test1 ' .. dump(self._engine_running) ) + if self._engine_running then + if self._last_applied_power ~= self._power_lever and not self._autopilot then + self._last_applied_power = self._power_lever + if not self._no_propeller then + self.object:set_animation_frame_speed(60 + self._power_lever) + end + airutils.engineSoundPlay(self) + end + else + if self.sound_handle then + minetest.sound_stop(self.sound_handle) + self.sound_handle = nil + self.object:set_animation_frame_speed(0) + end + end +end + +function airutils.add_paintable_part(self, entity_ref) + if not self._paintable_parts then self._paintable_parts = {} end + table.insert(self._paintable_parts, entity_ref:get_luaentity()) +end + +function airutils.set_param_paint(self, puncher, itmstck, mode) + mode = mode or 1 + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + if item_name == "automobiles_lib:painter" or item_name == "bike:painter" then + self._skin = "" + --painting with bike painter + local meta = itmstck:get_meta() + local colour = meta:get_string("paint_color") + + local colstr = self._color + local colstr_2 = self._color_2 + + if mode == 1 then colstr = colour end + if mode == 2 then colstr_2 = colour end + airutils.param_paint(self, colstr, colstr_2) + return true + else + --painting with dyes + local split = string.split(item_name, ":") + local indx, _ + if split[1] then _,indx = split[1]:find('dye') end + if indx then + self._skin = "" + --[[for clr,_ in pairs(airutils.colors) do + local _,x = split[2]:find(clr) + if x then color = clr end + end]]-- + --lets paint!!!! + local color = (item_name:sub(indx+1)):gsub(":", "") + + local colstr = self._color + local colstr_2 = self._color_2 + if mode == 1 then colstr = airutils.colors[color] end + if mode == 2 then colstr_2 = airutils.colors[color] end + + --minetest.chat_send_all(color ..' '.. dump(colstr)) + --minetest.chat_send_all(dump(airutils.colors)) + if colstr then + airutils.param_paint(self, colstr, colstr_2) + itmstck:set_count(itmstck:get_count()-1) + if puncher ~= nil then puncher:set_wielded_item(itmstck) end + return true + end + -- end painting + end + end + return false +end + +local function _paint(self, l_textures, colstr, paint_list, mask_associations) + paint_list = paint_list or self._painting_texture + mask_associations = mask_associations or self._mask_painting_associations + + for _, texture in ipairs(l_textures) do + for i, texture_name in ipairs(paint_list) do --textures list + local indx = texture:find(texture_name) + if indx then + l_textures[_] = texture_name.."^[multiply:".. colstr --paint it normally + local mask_texture = mask_associations[texture_name] --check if it demands a maks too + --minetest.chat_send_all(texture_name .. " -> " .. dump(mask_texture)) + if mask_texture then --so it then + l_textures[_] = "("..l_textures[_]..")^("..texture_name.."^[mask:"..mask_texture..")" --add the mask + end + end + end + end + return l_textures +end + +local function _set_skin(self, l_textures, paint_list, target_texture, skin) + skin = skin or self._skin + paint_list = paint_list or self._painting_texture + target_texture = target_texture or self._skin_target_texture + if not target_texture then return l_textures end + for _, texture in ipairs(l_textures) do + for i, texture_name in ipairs(paint_list) do --textures list + local indx = texture:find(target_texture) + if indx then + l_textures[_] = l_textures[_].."^"..skin --paint it normally + end + end + end + return l_textures +end + +local function set_prefix(self, l_textures) + --to reduce cpu processing, put the prefix here + if airutils._use_signs_api then + for _, texture in ipairs(l_textures) do + local indx = texture:find('airutils_name_canvas.png') + if indx then + l_textures[_] = "airutils_name_canvas.png^"..airutils.convert_text_to_texture(self._ship_name, self._name_color or 0, self._name_hor_aligment or 3.0) + end + end + end + return l_textures +end + +--painting +function airutils.param_paint(self, colstr, colstr_2) + colstr_2 = colstr_2 or colstr + if not self then return end + if self._skin ~= nil and self._skin ~= "" then + local l_textures = self.initial_properties.textures + l_textures = _set_skin(self, l_textures, self._painting_texture, self._skin_target_texture, self._skin) + + --to reduce cpu processing, put the prefix here + l_textures = set_prefix(self, l_textures) + + self.object:set_properties({textures=l_textures}) + + if self._paintable_parts then --paint individual parts + for i, part_entity in ipairs(self._paintable_parts) do + local p_textures = part_entity.initial_properties.textures + p_textures = _set_skin(part_entity, p_textures, self._painting_texture, self._skin_target_texture, self._skin) + part_entity.object:set_properties({textures=p_textures}) + end + end + return + end + + if colstr then + self._color = colstr + self._color_2 = colstr_2 + local l_textures = self.initial_properties.textures + + --to reduce cpu processing, put the prefix here + l_textures = set_prefix(self, l_textures) + + l_textures = _paint(self, l_textures, colstr) --paint the main plane + l_textures = _paint(self, l_textures, colstr_2, self._painting_texture_2) --paint the main plane + self.object:set_properties({textures=l_textures}) + + if self._paintable_parts then --paint individual parts + for i, part_entity in ipairs(self._paintable_parts) do + local p_textures = part_entity.initial_properties.textures + p_textures = _paint(part_entity, p_textures, colstr, self._painting_texture, self._mask_painting_associations) + p_textures = _paint(part_entity, p_textures, colstr_2, self._painting_texture_2, self._mask_painting_associations) + part_entity.object:set_properties({textures=p_textures}) + end + end + end +end + +function airutils.paint_with_mask(self, colstr, target_texture, mask_texture, mask_colstr) + if colstr then + self._color = colstr + if mask_colstr then + self._det_color = mask_colstr + end + local l_textures = self.initial_properties.textures + for _, texture in ipairs(l_textures) do + local indx = texture:find(target_texture) + if indx then + --"("..target_texture.."^[mask:"..mask_texture..")" + l_textures[_] = "("..target_texture.."^[multiply:".. colstr..")^("..target_texture.."^[mask:"..mask_texture..")" + end + end + self.object:set_properties({textures=l_textures}) + end +end + +function airutils.pid_controller(current_value, setpoint, last_error, d_time, kp, ki, kd, integrative) + kp = kp or 0 + ki = ki or 0.00000000000001 + kd = kd or 0.005 + + local ti = kp/ki + local td = kd/kp + local delta_t = d_time + + local _error = setpoint - current_value + local derivative = _error - last_error + --local output = kpv*erro + (kpv/Tiv)*I + kpv*Tdv*((erro - erro_passado)/delta_t); + if integrative == nil then integrative = 0 end + integrative = integrative + (((_error + last_error)/delta_t)/2); + local output = kp*_error + (kp/ti)*integrative + kp * td*((_error - last_error)/delta_t) + last_error = _error + return output, last_error +end + +function airutils.add_smoke_trail(self, smoke_type) + smoke_type = smoke_type or 1 + local smoke_texture = "airutils_smoke.png" + if smoke_type == 2 then + smoke_texture = "airutils_smoke.png^[multiply:#020202" + end + + if self._curr_smoke_type ~= smoke_type then + self._curr_smoke_type = smoke_type + if self._smoke_spawner then + minetest.delete_particlespawner(self._smoke_spawner) + self._smoke_spawner = nil + end + end + + if self._smoke_spawner == nil then + self._smoke_spawner = minetest.add_particlespawner({ + amount = 3, + time = 0, + --minpos = vector.subtract(pos, radius / 2), + --maxpos = vector.add(pos, radius / 2), + minvel = {x = -1, y = -1, z = -1}, + maxvel = {x = 1, y = 5, z = 1}, + minacc = vector.new(), + maxacc = vector.new(), + attached = self.object, + minexptime = 3, + maxexptime = 5.5, + minsize = 10, + maxsize = 15, + texture = smoke_texture, + }) + end +end + +function airutils.add_destruction_effects(pos, radius, w_fire) + if pos == nil then return end + w_fire = w_fire + if w_fire == nil then w_fire = true end + local node = airutils.nodeatpos(pos) + local is_liquid = false + if (node.drawtype == 'liquid' or node.drawtype == 'flowingliquid') then is_liquid = true end + + minetest.sound_play("airutils_explode", { + pos = pos, + max_hear_distance = 100, + gain = 2.0, + fade = 0.0, + pitch = 1.0, + }, true) + if is_liquid == false and w_fire == true then + minetest.add_particle({ + pos = pos, + velocity = vector.new(), + acceleration = vector.new(), + expirationtime = 0.4, + size = radius * 10, + collisiondetection = false, + vertical = false, + texture = "airutils_boom.png", + glow = 15, + }) + minetest.add_particlespawner({ + amount = 32, + time = 0.5, + minpos = vector.subtract(pos, radius / 2), + maxpos = vector.add(pos, radius / 2), + minvel = {x = -10, y = -10, z = -10}, + maxvel = {x = 10, y = 10, z = 10}, + minacc = vector.new(), + maxacc = vector.new(), + minexptime = 1, + maxexptime = 2.5, + minsize = radius * 3, + maxsize = radius * 5, + texture = "airutils_boom.png", + }) + end + minetest.add_particlespawner({ + amount = 64, + time = 1.0, + minpos = vector.subtract(pos, radius / 2), + maxpos = vector.add(pos, radius / 2), + minvel = {x = -10, y = -10, z = -10}, + maxvel = {x = 10, y = 10, z = 10}, + minacc = vector.new(), + maxacc = vector.new(), + minexptime = 1, + maxexptime = 2.5, + minsize = radius * 3, + maxsize = radius * 5, + texture = "airutils_smoke.png", + }) +end + +function airutils.add_blast_damage(pos, radius, damage_cal) + if not pos then return end + radius = radius or 10 + damage_cal = damage_cal or 4 + + local objs = minetest.get_objects_inside_radius(pos, radius) + for _, obj in pairs(objs) do + local obj_pos = obj:get_pos() + local dist = math.max(1, vector.distance(pos, obj_pos)) + local damage = (damage_cal / dist) * radius + + if obj:is_player() then + obj:set_hp(obj:get_hp() - damage) + else + local luaobj = obj:get_luaentity() + + -- object might have disappeared somehow + if luaobj then + local do_damage = true + local do_knockback = true + local entity_drops = {} + local objdef = minetest.registered_entities[luaobj.name] + + if objdef and objdef.on_blast then + do_damage, do_knockback, entity_drops = objdef.on_blast(luaobj, damage) + end + + if do_knockback then + local obj_vel = obj:get_velocity() + end + if do_damage then + obj:punch(obj, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = damage}, + }, nil) + end + --[[for _, item in pairs(entity_drops) do + add_drop(drops, item) -- !!! accessing undefined variable add_drop, drops + end]]-- + end + + end + end + --lets light some bombs + if airutils.is_minetest then + local pr = PseudoRandom(os.time()) + for z = -radius, radius do + for y = -radius, radius do + for x = -radius, radius do + -- remove the nodes + local r = vector.length(vector.new(x, y, z)) + if (radius * radius) / (r * r) >= (pr:next(80, 125) / 100) then + local p = {x = pos.x + x, y = pos.y + y, z = pos.z + z} + local node = minetest.get_node(p).name + if node == "tnt:tnt" then minetest.set_node(p, {name = "tnt:tnt_burning"}) end + end + end + end + end + end + +end + +function airutils.start_engine(self) + if self._engine_running then + self._engine_running = false + self._autopilot = false + self._power_lever = 0 --zero power + self._last_applied_power = 0 --zero engine + elseif self._engine_running == false and self._energy > 0 then + local curr_health_percent = (self.hp_max * 100)/self._max_plane_hp + if curr_health_percent > 20 then + self._engine_running = true + self._last_applied_power = -1 --send signal to start + else + if self.driver_name then + minetest.chat_send_player(self.driver_name,core.colorize('#ff0000', S(" >>> The engine is damaged, start procedure failed."))) + end + end + end +end + +function airutils.get_xz_from_hipotenuse(orig_x, orig_z, yaw, distance) + --cara, o minetest é bizarro, ele considera o eixo no sentido ANTI-HORÁRIO... Então pra equação funcionar, subtrair o angulo de 360 antes + yaw = math.rad(360) - yaw + local z = (math.cos(yaw)*distance) + orig_z + local x = (math.sin(yaw)*distance) + orig_x + return x, z +end + +function airutils.camera_reposition(player, pitch, roll) + local new_eye_offset = player:get_eye_offset() + + if roll < -0.4 then roll = -0.4 end + if roll > 0.4 then roll = 0.4 end + + local player_properties = player:get_properties() + + local eye_y = -5 + if airutils.detect_player_api(player) == 1 then + --minetest.chat_send_all("1") + eye_y = 0.5 + end + if airutils.detect_player_api(player) == 2 then + --minetest.chat_send_all("2") + eye_y = -5 + end + + local z, y = airutils.get_xz_from_hipotenuse(0, eye_y, pitch, player_properties.eye_height) + new_eye_offset.z = z*7 + new_eye_offset.y = y*1.5 + local x, _ = airutils.get_xz_from_hipotenuse(0, eye_y, roll, player_properties.eye_height) + new_eye_offset.x = -x*15 + + return new_eye_offset +end + +function airutils.seats_create(self) + if self.object then + local pos = self.object:get_pos() + self._passengers_base = {} + self._passengers = {} + if self._seats then + local max_seats = table.getn(self._seats) + for i=1, max_seats do + self._passengers_base[i] = minetest.add_entity(pos,'airutils:seat_base') + self._passengers[i] = "" + if not self._seats_rot then + self._passengers_base[i]:set_attach(self.object,'',self._seats[i],{x=0,y=0,z=0}) + else + self._passengers_base[i]:set_attach(self.object,'',self._seats[i],{x=0,y=self._seats_rot[i],z=0}) + end + end + + self.pilot_seat_base = self._passengers_base[1] --sets pilot seat reference + if self._have_copilot and self._passengers_base[2] then + self.co_pilot_seat_base = self._passengers_base[2] --sets copilot seat reference + end + end + end +end + +function airutils.seat_create(self, index) + if self.object then + local pos = self.object:get_pos() + if not self._passengers_base then + self._passengers_base = {} + if self._seats then + local max_seats = table.getn(self._seats) + for i=1, max_seats do + self._passengers_base[i] = 0 + end + end + end + if self._passengers_base[index] == 0 then + if self._seats then + local max_seats = table.getn(self._seats) + for i=1, max_seats do + if i == index then + self._passengers_base[i] = minetest.add_entity(pos,'airutils:seat_base') + local rot = self._seats_rot[i] or 0 + self._passengers_base[i]:set_attach(self.object,'',self._seats[i],{x=0,y=rot,z=0}) + break + end + end + end + end + end +end + +function airutils.seats_update(self) + if self.object then + if self._passengers_base then + local max_seats = table.getn(self._passengers_base) + for i=1, max_seats do + if self._passengers_base[i] then + if not self._seats_rot then + self._passengers_base[i]:set_attach(self.object,'',self._seats[i],{x=0,y=0,z=0}) + else + self._passengers_base[i]:set_attach(self.object,'',self._seats[i],{x=0,y=self._seats_rot[i],z=0}) + end + end + end + end + end +end + +function airutils.seats_destroy(self) + local max_seats = table.getn(self._seats) + for i=1, max_seats do + if self._passengers_base and self._passengers_base[i] then + if self._passengers_base[i] ~= 0 then self._passengers_base[i]:remove() end + end + end +end + +function airutils.flap_on(self) + if not self._wing_angle_extra_flaps then + self._flap = false + self._wing_configuration = self._wing_angle_of_attack + return + end + + if self._wing_angle_extra_flaps == nil then self._wing_angle_extra_flaps = 0 end --if not, just keep the same as normal angle of attack + local flap_limit = 15 + if self._flap_limit then flap_limit = self._flap_limit end + self._wing_configuration = self._wing_angle_of_attack + self._wing_angle_extra_flaps + if flap_limit >= 0 then + for i = 0,flap_limit do + minetest.after(0.02*i, function() + self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=-i, y=0, z=0}) + self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=-i, y=0, z=0}) + end) + end + else + for i = flap_limit,0 do + minetest.after(0.02*-i, function() + self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=-i, y=0, z=0}) + self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=-i, y=0, z=0}) + end) + end + --self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=-flap_limit, y=0, z=0}) + --self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=-flap_limit, y=0, z=0}) + end +end + +function airutils.flap_off(self) + self._wing_configuration = self._wing_angle_of_attack + local flap_limit = 15 + if self._flap_limit then flap_limit = self._flap_limit end + if flap_limit >= 0 then + for i = 0,flap_limit do + minetest.after(0.01*i, function() + self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=-flap_limit+i, y=0, z=0}) + self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=-flap_limit+i, y=0, z=0}) + end) + end + else + for i = flap_limit,0 do + minetest.after(0.01*-i, function() + self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=-flap_limit+i, y=0, z=0}) + self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=-flap_limit+i, y=0, z=0}) + end) + end + --self.object:set_bone_position("flap.l", {x=0, y=0, z=0}, {x=0, y=0, z=0}) + --self.object:set_bone_position("flap.r", {x=0, y=0, z=0}, {x=0, y=0, z=0}) + end +end + +function airutils.flap_operate(self, player) + if self._flap == false then + minetest.chat_send_player(player:get_player_name(), S(">>> Flap down")) + self._flap = true + airutils.flap_on(self) + minetest.sound_play("airutils_collision", { + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 0.5, + }, true) + else + minetest.chat_send_player(player:get_player_name(), S(">>> Flap up")) + self._flap = false + airutils.flap_off(self) + minetest.sound_play("airutils_collision", { + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 0.7, + }, true) + end +end + +function airutils.landing_lights_operate(self) + if self._last_light_move == nil then self._last_light_move = 0.15 end + self._last_light_move = self._last_light_move + self.dtime + if self._last_light_move > 0.15 then + self._last_light_move = 0 + if self._land_light == true and self._engine_running == true then + self._light_active_time = self._light_active_time + self.dtime + --minetest.chat_send_all(self._light_active_time) + if self._light_active_time > 24 then self._land_light = false end + airutils.put_light(self) + else + self._land_light = false + self._light_active_time = 0 + airutils.remove_light(self) + end + end +end + +function airutils.get_adf_angle(self, pos) + local adf = 0 + if self._adf == true then + if airutils.getAngleFromPositions and self._adf_destiny then + adf = airutils.getAngleFromPositions(pos, self._adf_destiny) + adf = -(adf + math.deg(self._yaw)) + end + end + return adf +end + +function airutils.destroyed_save_static_data(self) + return minetest.serialize( + { + stored_owner = self.owner, + stored_slots = self._trunk_slots, + stored_inv_id = self._inv_id, + stored_game_time = self._game_time, + } + ) +end + +function airutils.destroyed_on_activate(self, staticdata, dtime_s) + if staticdata ~= "" and staticdata ~= nil then + local data = minetest.deserialize(staticdata) or {} + self.owner = data.stored_owner + self._inv_id = data.stored_inv_id + self._trunk_slots = data.stored_slots + self._game_time = data.stored_game_time + end + + local inv = minetest.get_inventory({type = "detached", name = self._inv_id}) + -- if the game was closed the inventories have to be made anew, instead of just reattached + if inv then + self._inv = inv + end + + airutils.set_acceleration(self.object,{x=0,y=airutils.gravity,z=0}) + self.object:set_bone_position("elevator", self._elevator_pos, {x=-170, y=0, z=0}) +end + +local function check_shared_by_time(self) + local shared_by_time = false + if self._game_time then + --check if it was created in the last 20 minutes (1200 seconds) + if minetest.get_gametime() - self._game_time >= 1200 then shared_by_time = true end + end + return shared_by_time +end + +function airutils.destroyed_open_inventory(self, clicker) + if not clicker or not clicker:is_player() then + return + end + + local name = clicker:get_player_name() + + if self.owner == "" then + self.owner = name + end + + local shared_by_time = check_shared_by_time(self) + + if name == self.owner or shared_by_time then + if not self._inv then + airutils.create_inventory(self, self._trunk_slots) + end + airutils.show_vehicle_trunk_formspec(self, clicker, self._trunk_slots) + else + minetest.chat_send_player(name, core.colorize('#ff0000', S('>>> You cannot claim this scrap yet, wait some minutes.'))) + end +end + +function airutils.destroyed_on_punch(self, puncher, ttime, toolcaps, dir, damage) + if not puncher or not puncher:is_player() then + return + end + + local name = puncher:get_player_name() + local shared_by_time = check_shared_by_time(self) + local pos = self.object:get_pos() + + local is_admin = minetest.check_player_privs(puncher, {server=true}) + if shared_by_time == false then + if self.owner and self.owner ~= name and self.owner ~= "" then + if is_admin == false then return end + end + end + + minetest.sound_play("airutils_collision", { + object = self.object, + max_hear_distance = 5, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }) + + spawn_drops(self, pos) + + airutils.destroy_inventory(self) + self.object:remove() +end diff --git a/mods/airutils/light.lua b/mods/airutils/light.lua new file mode 100644 index 00000000..9e92ead3 --- /dev/null +++ b/mods/airutils/light.lua @@ -0,0 +1,83 @@ + +function airutils.get_xz_from_hipotenuse(orig_x, orig_z, yaw, distance) + --cara, o minetest é bizarro, ele considera o eixo no sentido ANTI-HORÁRIO... Então pra equação funcionar, subtrair o angulo de 360 antes + yaw = math.rad(360) - yaw + local z = (math.cos(yaw)*distance) + orig_z + local x = (math.sin(yaw)*distance) + orig_x + return x, z +end + +minetest.register_node("airutils:light", { + drawtype = "airlike", + --tile_images = {"airutils_light.png"}, + inventory_image = minetest.inventorycube("airutils_light.png"), + paramtype = "light", + walkable = false, + is_ground_content = true, + light_propagates = true, + sunlight_propagates = true, + light_source = 14, + selection_box = { + type = "fixed", + fixed = {0, 0, 0, 0, 0, 0}, + }, +}) + +function airutils.remove_light(self) + if self._light_old_pos then + --force the remotion of the last light + minetest.add_node(self._light_old_pos, {name="air"}) + self._light_old_pos = nil + end +end + +function airutils.swap_node(self, pos) + local target_pos = pos + local have_air = false + local node = nil + local count = 0 + while have_air == false and count <= 3 do + node = minetest.get_node(target_pos) + if node.name == "air" then + have_air = true + break + end + count = count + 1 + target_pos.y = target_pos.y + 1 + end + + if have_air then + minetest.set_node(target_pos, {name='airutils:light'}) + airutils.remove_light(self) + self._light_old_pos = target_pos + return true + end + return false +end + +function airutils.put_light(self) + local pos = self.object:get_pos() + pos.y = pos.y + 1 + local yaw = self.object:get_yaw() + local lx, lz = airutils.get_xz_from_hipotenuse(pos.x, pos.z, yaw, 10) + local light_pos = {x=lx, y=pos.y, z=lz} + + local cast = minetest.raycast(pos, light_pos, false, false) + local thing = cast:next() + local was_set = false + while thing do + if thing.type == "node" then + local ipos = thing.intersection_point + if ipos then + was_set = airutils.swap_node(self, ipos) + end + end + thing = cast:next() + end + if was_set == false then + local n = minetest.get_node_or_nil(light_pos) + if n and n.name == 'air' then + airutils.swap_node(self, light_pos) + end + end +end diff --git a/mods/airutils/locale/airutils.de.tr b/mods/airutils/locale/airutils.de.tr new file mode 100644 index 00000000..63b1a2cf --- /dev/null +++ b/mods/airutils/locale/airutils.de.tr @@ -0,0 +1,85 @@ +# textdomain: airutils +Bio Fuel=Biotreibstoff +Biofuel Distiller=Biotreibstoff-Brenner +Fuel Distiller=Treibstoff-Destilliergerät +Fuel Distiller (FULL)=Treibstoff-Destilliergerät (VOLL) +Fuel Distiller @1% done=Treibstoff-Destilliergerät @1% fertig +PAPI=PAPI +left side=linke Seite +right side=rechte Seite +right_side=rechte_seite +Owned by: @1=Gehört: @1 +Repair Tool=Reparaturwerkzeug +Tug tool for airport=Schleppwerkzeug für den Flughafen +@1 is protected by @2.=@1 ist geschützt von @2. +@1 is protected.=@1 ist geschützt. +Wind Indicator=Windanzeiger + >>> The wind direction now is @1= >>> Die Windrichtung ist nun @1 +Wind Direction Indicator=Windrichtungsanzeiger +Node is protected=Block ist geschützt + Current hp: = Aktuelle Lp: +Nice @1 of @2.@3= Schöne(r/s) @1 von @2.@3 + >>> The captain got the control.= >>> Der Kaptain übernimmt die Kontrolle. + >>> The control is with you now.= >>> Du hast nun die Kontrolle. + >>> The control was given.= >>> Die Kontrolle wurde erteilt. +Enable/disable explosion blast damage=Explosionsschaden aktivieren/deaktivieren +>>> Blast damage by explosion is disabled=Explosionsschaden ist deaktiviert. +>>> Blast damage by explosion is enabled=Explosionsschaden ist aktiviert. +Transfer the property of a plane to another player=Überträgt die Eigentümerschaft des Fluggerätes an einen anderen Spieler. + >>> This plane now is property of: = >>> Dieses Fluggerät ist nun Eigentum von: + >>> only the owner or moderators can transfer this airplane= >>> Nur der Eigentümer oder Moderator kann die Eigentümerschaft ändern. + >>> the target player must be logged in= >>> der Zielspieler muß eingeloggt sein. + >>> you are not inside a plane to perform the command= >>> du sitzt in keinem Fluggerät um dieses Kommando zu geben +Ejects from a plane=Wirft dich aus dem Fluggerät + >>> you are not inside a plane= >>> du sitzt in keinem Fluggerät +Enables/disables the ground effect (for debug purposes)=Ein/Ausschaltet den Bodeneffekt (zur Fehlersuche) + >>> Ground effect was turned on.= >>> Bodeneffekt wurde eingeschalten. +>>> Ground effect was turned off.=>>> Bodeneffekt wurde ausgeschalten. + >>> You need 'server' priv to run this command.= >>> Du brauchst 'server'-rechte um dieses Kommando auszuführen. +Enables/disables the lift printing (for debug purposes)=Ein/Ausschalten des Lift schreibens (zur Fehlersuche) + >>> Lift printing turned on.= >>> Lift schreiben eingeschalten. + >>> Lift printing turned off.= >>> Lift schreiben ausgeschalten. + >>> Mouse control disabled.= >>> Mauslenkung ausgeschalten. + >>> Mouse control enabled.= >>> Mauslenkung eingeschalten. + >>> Autopilot deactivated= >>> Autopilot ausgeschalten + >>> Autopilot on= >>> Autopilot eingeschalten + >>> Flaps retracted due for overspeed= >>> Klappen eingefahren wegen Hochgeschwindigkeit +You need steel ingots in your inventory to perform this repair.=Du brauchst Eisenbarren in einem Inventar um das zu reparieren. +Start/Stop Engines=Starte/Stoppe Maschinen +Show/Hide Gauges=Blende Messgeräte ein/aus +Show Inventory=Zeige Inventar +Yaw by mouse=Lenke mit Maus +Go Out!=Steig aus! +Flaps down=Klappen unten +Landing Light=Landelicht +Autopilot=Autopilot +Co-pilot Manager=Co-Pilot Manager +Adf Manager=Adf Manager +Manual=Manuell +Bring a copilot=Bring einen Copiloten +Pass the Control=Übergebe die Kontrolle +Auto Direction Find=Automatischer Richtungsfinder +OK=OK +Change Seat=Wechlse Sitzplatz +Go Offboard=Aussteigen +Do you really want to go offboard now?=Du willst jetzt wirklich aussteigen? +No=Nein +Yes=Ja + >>> There is something wrong with the plane...= >>> Da stimmt was nicht mit dem Fluggerät + >>> ADF deactivated.= >>> ADF deaktiviert + >>> ADF activated.= >>> ADF aktiviert. + >>> Destination written successfully.= >>> Ziel erfolgreich eingetragen + >>> There is something wrong with the ADF fields values.=Mit den Werten der ADF-Felder ist etwas nicht in Ordnung + >>> Both ADF fields must be given to complete the operation.= >>> Beide ADF-Felder müssen angegeben werden, damit der Vorgang abgeschlossen werden kann + >>> There is something wrong on ADF saving...= >>> Da stimmt etwas nicht mit dem ADF speichern +Flight Information=Fluginformation + >>> You need the priv= >>> Du brauchst das Recht +to fly this plane.=um das Fluggerät zu fliegen. + >>> The engine is damaged, start procedure failed.= >>> Die Maschine ist beschädigt, starten fehlgeschlagen. +>>> Flap down=>>> Klappen unten +>>> Flap up=>>> Klappen oben +>>> You cannot claim this scrap yet, wait some minutes.=Du kannst diesen Schrott noch nicht beanspruchen, warte ein paar Minuten +Sorry, but this module doesn't work when SkinsDb and Armor are instaled together.=Entschuldige, aber dieses Modul funktioniert nicht, wenn SkinsDb und Armor zusammen installiert ist. +Something isn't working...=Etwas funktioniert nicht... +Set Player Texture=Wähle Spielertextur +The isn't activated as secure. Aborting=Das ist zur Sicherheit nicht aktiviert. Abbruch diff --git a/mods/airutils/locale/airutils.fr.tr b/mods/airutils/locale/airutils.fr.tr new file mode 100644 index 00000000..f337deca --- /dev/null +++ b/mods/airutils/locale/airutils.fr.tr @@ -0,0 +1,85 @@ +# textdomain: airutils +Bio Fuel=Biocarburant +Biofuel Distiller=Raffineur de Biocarburant +Fuel Distiller=Raffineur de Carburant +Fuel Distiller (FULL)=Raffineur de Carburant (PLEIN) +Fuel Distiller @1% done=Raffineur de Carburant : @1% traité +PAPI=PAPI +left side=coté gauche +right side=coté droit +right_side=coté_droit +Owned by: @1=Propriété de @1 +Repair Tool=Outil de Réparation +Tug tool for airport=Remorqueur d'aéroport +@1 is protected by @2.=@1 est protégé par @2. +@1 is protected.=@1 est protégé. +Wind Indicator=Girouette + >>> The wind direction now is @1= >>> La direction actuelle du vent est @1 +Wind Direction Indicator=Manche à Air +Node is protected=Nœud protégé + Current hp: = Points de vie : +Nice @1 of @2.@3=Joli(e) @1 de @2.@3 + >>> The captain got the control.=>>> Le commandant de bord a repris la main. + >>> The control is with you now.=>>> Vous avez la main. + >>> The control was given.=>>> Vous avez donné la main. +Enable/disable explosion blast damage=Activer/Désactiver les dégats d'explosion +>>> Blast damage by explosion is disabled=>>> Les dégats d'explosion sont désactivés +>>> Blast damage by explosion is enabled=>>> Les dégats d'explosion sont activés +Transfer the property of a plane to another player=Transférer la propriéte d'un avion à un autre joueur + >>> This plane now is property of: = >>> Cet avion est désormais la propriété de : + >>> only the owner or moderators can transfer this airplane= >>> Seuls son propriétaire ou un modérateur peuvent transférer cet avion + >>> the target player must be logged in= >>> Le nouveau propriétaire doit être connecté + >>> you are not inside a plane to perform the command= >>> Vous devez être dans un avion pour exécuter cette commande +Ejects from a plane=Ejectez-vous de l'avion + >>> you are not inside a plane= >>> Vous n'êtes pas à l'intérieur d'un avion +Enables/disables the ground effect (for debug purposes)=Activer/Désactiver l'effet de sol (pour déboguer) + >>> Ground effect was turned on.= >>> L'effet de sol est activé. +>>> Ground effect was turned off.= >>> L'effet de sol est désactivé. + >>> You need 'server' priv to run this command.= >>> Vous devez avoir le privilège 'server' pour exécuter cette commande. +Enables/disables the lift printing (for debug purposes)=Activer/Désactiver le débogage de sustenstation + >>> Lift printing turned on.= >>> Débogage de sustenstation activé. + >>> Lift printing turned off.= >>> Débogage de sustenstation desactivé. + >>> Mouse control disabled.= >>> Commande à la souris désactivée. + >>> Mouse control enabled.= >>> Commande à la souris activée. + >>> Autopilot deactivated= >>> Autopilote désactivé + >>> Autopilot on= >>> Autopilote activé + >>> Flaps retracted due for overspeed= >>> Volets rétractés pour cause de sur-vitesse +You need steel ingots in your inventory to perform this repair.=Vous avez besoin de lingots d'acier pour réaliser cette réparation. +Start/Stop Engines=Démarrer/Arrêter les moteurs +Show/Hide Gauges=Montrer/Cacher les Instruments +Show Inventory=Montrer l'inventaire +Yaw by mouse=Palonnier à la souris +Go Out!=Débarquer ! +Flaps down=Volets déployés +Landing Light=Phare d'atterrissage +Autopilot=Autopilote +Co-pilot Manager=Gestionnaire de copilote +Adf Manager=Gestionnaire ADF +Manual=Manuel +Bring a copilot=Embarquer un copilote +Pass the Control=Donner la main +Auto Direction Find=ADF +OK=OK +Change Seat=Échanger les sièges +Go Offboard=Débarquer +Do you really want to go offboard now?=Voulez-vous vraiment débarquer maintenant ? +No=Non +Yes=Oui + >>> There is something wrong with the plane...= >>> Quelque chose est anormal dans cet avion... + >>> ADF deactivated.= >>> ADF désactivé + >>> ADF activated.= >>> ADF activé + >>> Destination written successfully.= >>> Destination enregistrée. + >>> There is something wrong with the ADF fields values.= >>> Quelque chose est anormal dans la configuration de l'ADF. + >>> Both ADF fields must be given to complete the operation.= >>> Les deux informations ADF doivent être fournies pour terminer l'opération. + >>> There is something wrong on ADF saving...= >>> Quelque chose est anormal dans la sauvegarde de l'ADF... +Flight Information=Instruments + >>> You need the priv= >>> Vous devez avoir le privilège +to fly this plane.=pour piloter cet avion. + >>> The engine is damaged, start procedure failed.= >>> Moteur endommagé, Échec du démarrage. +>>> Flap down=Volets déployés +>>> Flap up=Volets relevés +>>> You cannot claim this scrap yet, wait some minutes.= >>> Veuillez attendre quelques minutes pour récupérer ces débris +Sorry, but this module doesn't work when SkinsDb and Armor are instaled together.=Désolé, ce module ne fontionne pas avec les mods SkinDb et Armor +Something isn't working...=Quelque chose défaille... +Set Player Texture=Modifier la texture du joueur +The isn't activated as secure. Aborting=Inactif pour raison de sécurité. Interruption. diff --git a/mods/airutils/locale/airutils.pt_BR.tr b/mods/airutils/locale/airutils.pt_BR.tr new file mode 100644 index 00000000..12d933ff --- /dev/null +++ b/mods/airutils/locale/airutils.pt_BR.tr @@ -0,0 +1,84 @@ +# textdomain: airutils +Bio Fuel=Bio Combustível +Biofuel Distiller=Destilador de Bio Combustível +Fuel Distiller=Destilador de Combustível +Fuel Distiller (FULL)=Destilador de Combustível (Cheio) +Fuel Distiller @1% done=Destilador de Combustível @1% concluído +PAPI=PAPI +left side=lado esquerdo +right side=lado direito +Owned by: @1=Pertence à @1 +Repair Tool=Ferramente de Reparos +Tug tool for airport=Ferramenta para rebocar aeronaves +@1 is protected by @2.=@1 está protegido por @2. +@1 is protected.=@1 é protegido. +Wind Indicator=Biruta + >>> The wind direction now is @1= >>> A direção do vento agora é @1 +Wind Direction Indicator=Indicador de direção de vento +Node is protected=Bloco protegido + Current hp: = Hp atual: +Nice @1 of @2.@3= @1 de @2.@3 + >>> The captain got the control.= >>> O controle está com o comandante. + >>> The control is with you now.= >>> O controle está contigo. + >>> The control was given.= >>> Você passou o controle. +Enable/disable explosion blast damage=Ligar/desligar os danos de explosão +>>> Blast damage by explosion is disabled=Danos de explosão desligados. +>>> Blast damage by explosion is enabled=Danos de explosão ligados. +Transfer the property of a plane to another player=Transferir a propriedade da aeronave para outro jogador. + >>> This plane now is property of: = >>> Essa aeronave agora é propriedade de: + >>> only the owner or moderators can transfer this airplane= >>> Apenas o dono ou moderadores podem transferir essa aeronave + >>> the target player must be logged in= >>> O jogador de destino deve estar logado para continuar. + >>> you are not inside a plane to perform the command= >>> você não está na aeronave para executar este comando +Ejects from a plane=Ejetar da aeronave + >>> you are not inside a plane= >>> você não está em uma aeronave +Enables/disables the ground effect (for debug purposes)=Liga/desliga o efeito solo (para debug) + >>> Ground effect was turned on.= >>> Efeito solo ligado. +>>> Ground effect was turned off.=>>> Efeito solo desligado. + >>> You need 'server' priv to run this command.= >>> Você precisa do privilégio 'server' para executar este comando. +Enables/disables the lift printing (for debug purposes)=Liga/desliga o print da sustentação (para debug) + >>> Lift printing turned on.= >>> Print da sustentação ligado. + >>> Lift printing turned off.= >>> Print da sustentação desligado. + >>> Mouse control disabled.= >>> Controle pelo mouse desligado. + >>> Mouse control enabled.= >>> Controle pelo mouse ligado. + >>> Autopilot deactivated= >>> Piloto automático desligado + >>> Autopilot on= >>> Piloto automático ligado + >>> Flaps retracted due for overspeed= >>> Flaps recolhidos devido a alta velocidade +You need steel ingots in your inventory to perform this repair.=Você precisa de lingotes de aço para realizar este reparo. +Start/Stop Engines=Ligar/Desligar motor(es) +Show/Hide Gauges=Exibir/Remover HUD +Show Inventory=Abrir inventário +Yaw by mouse=Direção pelo mouse +Go Out!=Sair da aeronave! +Flaps down=Flaps baixados +Landing Light=Luz de pouso +Autopilot=Piloto automático +Co-pilot Manager=Gerenciar co-piloto +Adf Manager=Gerenciamento de ADF +Manual=Manual +Bring a copilot=Trazer um co-piloto +Pass the Control=Passar o controle +Auto Direction Find=Localiz. Autom. de Direção +OK=OK +Change Seat=Trocar de assento +Go Offboard=Abandonar aeronave +Do you really want to go offboard now?=Tem certeza que deseja sair da aeronave agora? +No=Não +Yes=Sim + >>> There is something wrong with the plane...= >>> Há algo errado com a aeronave... + >>> ADF deactivated.= >>> ADF desativado + >>> ADF activated.= >>> ADF ativado. + >>> Destination written successfully.= >>> Destino gravado com sucesso. + >>> There is something wrong with the ADF fields values.= >>> Há algo errado com os valores do ADF. + >>> Both ADF fields must be given to complete the operation.= >>> Ambos os campos do ADF devem ser preenchidos. + >>> There is something wrong on ADF saving...= >>> Algo deu errado ao salvar os campos do ADF... +Flight Information=Informações de vôo + >>> You need the priv= >>> Você precisa do privilégio +to fly this plane.= para voar esta aeronave. + >>> The engine is damaged, start procedure failed.= >>> Motor danificado, procedimento de partida falhou. +>>> Flap down=>>> Flaps baixados +>>> Flap up=>>> Flaps recolhidos +>>> You cannot claim this scrap yet, wait some minutes.= >>> Você não pode coletar esta sucata ainda, aguarde alguns minutos. +Sorry, but this module doesn't work when SkinsDb and Armor are instaled together.=Desculpe, mas este módulo não funciona quando o SkinsDb e o mod Armor estão instalados juntos. +Something isn't working...=Alguma coisa não está funcionando... +Set Player Texture=Coloque esta textura no jogador +The isn't activated as secure. Aborting=Este mod não está habilitado como seguro, abortando. diff --git a/mods/airutils/locale/airutils.ru.tr b/mods/airutils/locale/airutils.ru.tr new file mode 100644 index 00000000..a2220f36 --- /dev/null +++ b/mods/airutils/locale/airutils.ru.tr @@ -0,0 +1,85 @@ +# textdomain: airutils +Bio Fuel=Биотопливо +Biofuel Distiller=Дистиллятор биотоплива +Fuel Distiller=Дистиллятор топлива +Fuel Distiller (FULL)=Дистиллятор топлива (ЗАПОЛНЕН) +Fuel Distiller @1% done=Дистиллятор топлива завершен на @1% +PAPI=PAPI +left side=левая сторона +right side=правая сторона +right_side=правая_сторона +Owned by: @1=Принадлежит: @1 +Repair Tool=Инструмент для ремонта +Tug tool for airport=Буксировочный инструмент для аэропорта +@1 is protected by @2.=@1 защищен @2. +@1 is protected.=@1 защищен. +Wind Indicator=Индикатор ветра + >>> The wind direction now is @1= >>> Направление воздуха сейчас - это @1 +Wind Direction Indicator=Индикатор направления ветра +Node is protected=Нода защищена + Current hp: =Текущий hp: +Nice @1 of @2.@3=Хороши @1 у @2.@3 + >>> The captain got the control.= >>> Капитан получил контроль. + >>> The control is with you now.= >>> Теперь вы контролируете. + >>> The control was given.= >>> Контроль был передан. +Enable/disable explosion blast damage=Включить/выключить повреждение от взрыва +>>> Blast damage by explosion is disabled= >>> Выключено повреждение от взрыва +>>> Blast damage by explosion is enabled= >>> Включено повреждение от взрыва +Transfer the property of a plane to another player=Передать владение самолетом другому игроку + >>> This plane now is property of: = >>> Этот самолет теперь принадлежит: + >>> only the owner or moderators can transfer this airplane= >>> только владелец или модераторы могут передать этот самолет + >>> the target player must be logged in= >>> целевой игрок должен быть авторизован + >>> you are not inside a plane to perform the command= >>> вы вне самолета из-за чего не можете выполнить команду +Ejects from a plane=Катапультироваться из самолета + >>> you are not inside a plane= >>> вы вне самолета +Enables/disables the ground effect (for debug purposes)=Включить/выключить эффект приземления (для отладки) + >>> Ground effect was turned on.= >>> Эффект приземления был включен. +>>> Ground effect was turned off.=>>> Эффект приземления выключен. + >>> You need 'server' priv to run this command.= >>> Вам нужна привилегия 'сервер' что бы выполнить эту команду. +Enables/disables the lift printing (for debug purposes)=Печатать о подъеме (для отладки) + >>> Lift printing turned on.= >>> Печатание о подъеме включено. + >>> Lift printing turned off.= >>> Печатание о подъеме выключено. + >>> Mouse control disabled.= >>> Контроль над мышкой выключен. + >>> Mouse control enabled.= >>> Контроль над мышкой включен. + >>> Autopilot deactivated= >>> Автопилот выключен + >>> Autopilot on= >>> Автопилот включен + >>> Flaps retracted due for overspeed= >>> Закрылки убраны из-за превышения скорости +You need steel ingots in your inventory to perform this repair.=Вам нужны железные слитки в вашем инвентаре, что бы починить. +Start/Stop Engines=Включить/выключить двигатели +Show/Hide Gauges=Показать/спрятать датчики +Show Inventory=Показать инвентарь +Yaw by mouse=Рыскание мышью +Go Out!=Выйти! +Flaps down=Закрылки опущены +Landing Light=Посадочный фонарь +Autopilot=Автопилот +Co-pilot Manager=Менеджер второго пилота +Adf Manager=Радиокомпас +Manual=Учебник +Bring a copilot=Привести второго пилота +Pass the Control=Передать управление +Auto Direction Find=Автоматический поиск направления +OK=OK +Change Seat=Сменить сидение +Go Offboard=Выйти за борт +Do you really want to go offboard now?=Вы серьезно хотите выйти за борт? +No=Нет +Yes=Да + >>> There is something wrong with the plane...= >>> Что-то не так с этим самолетом... + >>> ADF deactivated.= >>> Радиокомпас выключен. + >>> ADF activated.= >>> Радиокомпас включен. + >>> Destination written successfully.= >>> Место назначения записано успешно. + >>> There is something wrong with the ADF fields values.= >>> Что-то не так со значениями полей ADF. + >>> Both ADF fields must be given to complete the operation.= >>> Для завершения операции необходимо указать оба поля ADF. + >>> There is something wrong on ADF saving...= >>> Что-то не так с сохранением ADF... +Flight Information=Информация полета + >>> You need the priv= >>> Вам нужна привилегия +to fly this plane.=что бы лететь на этом самолете. + >>> The engine is damaged, start procedure failed.= >>> Двигатель поврежден, запуск провален. +>>> Flap down=>>> Закрылки опущены +>>> Flap up=>>> Закрылки подняты +>>> You cannot claim this scrap yet, wait some minutes.=>>> Вы пока не можете забрать этот лом, подождите несколько минут. +Sorry, but this module doesn't work when SkinsDb and Armor are instaled together.=Извините, но этот модуль не работает, когда SkinsDb и Armor установлены вместе. +Something isn't working...=Что-то не работает... +Set Player Texture=Выберите текстуру игрока +The isn't activated as secure. Aborting=Активация не безопасна. Прерывание diff --git a/mods/airutils/locale/template.txt b/mods/airutils/locale/template.txt new file mode 100644 index 00000000..3563107b --- /dev/null +++ b/mods/airutils/locale/template.txt @@ -0,0 +1,85 @@ +# textdomain: airutils +Bio Fuel= +Biofuel Distiller= +Fuel Distiller= +Fuel Distiller (FULL)= +Fuel Distiller @1% done= +PAPI= +left side= +right side= +right_side= +Owned by: @1= +Repair Tool= +Tug tool for airport= +@1 is protected by @2.= +@1 is protected.= +Wind Indicator= + >>> The wind direction now is @1= +Wind Direction Indicator= +Node is protected= + Current hp: = +Nice @1 of @2.@3= + >>> The captain got the control.= + >>> The control is with you now.= + >>> The control was given.= +Enable/disable explosion blast damage= +>>> Blast damage by explosion is disabled= +>>> Blast damage by explosion is enabled= +Transfer the property of a plane to another player= + >>> This plane now is property of: = + >>> only the owner or moderators can transfer this airplane= + >>> the target player must be logged in= + >>> you are not inside a plane to perform the command= +Ejects from a plane= + >>> you are not inside a plane= +Enables/disables the ground effect (for debug purposes)= + >>> Ground effect was turned on.= +>>> Ground effect was turned off.= + >>> You need 'server' priv to run this command.= +Enables/disables the lift printing (for debug purposes)= + >>> Lift printing turned on.= + >>> Lift printing turned off.= + >>> Mouse control disabled.= + >>> Mouse control enabled.= + >>> Autopilot deactivated= + >>> Autopilot on= + >>> Flaps retracted due for overspeed= +You need steel ingots in your inventory to perform this repair.= +Start/Stop Engines= +Show/Hide Gauges= +Show Inventory= +Yaw by mouse= +Go Out!= +Flaps down= +Landing Light= +Autopilot= +Co-pilot Manager= +Adf Manager= +Manual= +Bring a copilot= +Pass the Control= +Auto Direction Find= +OK= +Change Seat= +Go Offboard= +Do you really want to go offboard now?= +No= +Yes= + >>> There is something wrong with the plane...= + >>> ADF deactivated.= + >>> ADF activated.= + >>> Destination written successfully.= + >>> There is something wrong with the ADF fields values.= + >>> Both ADF fields must be given to complete the operation.= + >>> There is something wrong on ADF saving...= +Flight Information= + >>> You need the priv= +to fly this plane.= + >>> The engine is damaged, start procedure failed.= +>>> Flap down= +>>> Flap up= +>>> You cannot claim this scrap yet, wait some minutes.= +Sorry, but this module doesn't work when SkinsDb and Armor are instaled together.= +Something isn't working...= +Set Player Texture= +The isn't activated as secure. Aborting= diff --git a/mods/airutils/mod.conf b/mods/airutils/mod.conf new file mode 100644 index 00000000..03dfeca7 --- /dev/null +++ b/mods/airutils/mod.conf @@ -0,0 +1,6 @@ +name = airutils +title=AirUtils +description=A lib for airplanes and some useful tools +author=apercy +optional_depends=player_api, mcl_formspec, mcl_player, emote, climate_api, biofuel, signs_lib, skinsdb +release = 31048 diff --git a/mods/airutils/mod_translation_updater.py b/mods/airutils/mod_translation_updater.py new file mode 100644 index 00000000..b2feaaf1 --- /dev/null +++ b/mods/airutils/mod_translation_updater.py @@ -0,0 +1,495 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Script to generate Minetest translation template files and update +# translation files. +# +# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer, +# 2023 Wuzzy. +# License: LGPLv2.1 or later (see LICENSE file for details) + +import os, fnmatch, re, shutil, errno +from sys import argv as _argv +from sys import stderr as _stderr + +# Running params +params = {"recursive": False, + "help": False, + "verbose": False, + "folders": [], + "old-file": False, + "break-long-lines": False, + "print-source": False, + "truncate-unused": False, +} +# Available CLI options +options = {"recursive": ['--recursive', '-r'], + "help": ['--help', '-h'], + "verbose": ['--verbose', '-v'], + "old-file": ['--old-file', '-o'], + "break-long-lines": ['--break-long-lines', '-b'], + "print-source": ['--print-source', '-p'], + "truncate-unused": ['--truncate-unused', '-t'], +} + +# 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 = 80 + +# These symbols mark comment lines showing the source file name. +# A comment may look like "##[ init.lua ]##". +symbol_source_prefix = "##[" +symbol_source_suffix = "]##" + +# comment to mark the section of old/unused strings +comment_unused = "##### not used anymore #####" + +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["old-file"])} + create *.old files + {', '.join(options["break-long-lines"])} + add extra line breaks before and after long strings + {', '.join(options["print-source"])} + add comments denoting the source file + {', '.join(options["verbose"])} + add output information + {', '.join(options["truncate-unused"])} + delete unused strings from files +''') + +def main(): + '''Main function''' + set_params(_argv) + set_params_folders(_argv) + if params["help"]: + print_help(_argv[0]) + else: + # Add recursivity message + print("Running ", end='') + if params["recursive"]: + print("recursively ", end='') + # Running + if 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_quoted = re.compile( + r'(?:^|[\.=,{\(\s])' # Look for beginning of file or anything that isn't a function identifier + r'N?F?S\s*\(\s*' # Matches S, FS, NS or NFS function call + r'(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)' # Quoted string + r'[\s,\)]', # End of call or argument + re.DOTALL) +# Handles the [[ ... ]] string delimiters +pattern_lua_bracketed = re.compile( + r'(?:^|[\.=,{\(\s])' # Same as for pattern_lua_quoted + r'N?F?S\s*\(\s*' # Same as for pattern_lua_quoted + r'\[\[(.*?)\]\]' # [[ ... ]] string delimiters + r'[\s,\)]', # Same as for pattern_lua_quoted + re.DOTALL) + +# Handles "concatenation" .. " of strings" +pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) + +# Handles a translation line in *.tr file. +# Group 1 is the source string left of the equals sign. +# Group 2 is the translated string, right of the equals sign. +pattern_tr = re.compile( + r'(.*)' # Source string + # the separating equals sign, if NOT preceded by @, unless + # that @ is preceded by another @ + r'(?:(?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, header_comments, textdomain): + # if textdomain is specified, insert it at the top + if textdomain != None: + lOut = [textdomain] # argument is full textdomain line + # otherwise, use mod name as textdomain automatically + else: + lOut = [f"# textdomain: {mod_name}"] + if header_comments is not None: + lOut.append(header_comments) + + dGroupedBySource = {} + + for key in dkeyStrings: + sourceList = list(dkeyStrings[key]) + 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] + if params["print-source"]: + if lOut[-1] != "": + lOut.append("") + lOut.append(source) + for localizedString in localizedStrings: + val = dOld.get(localizedString, {}) + translation = val.get("translation", "") + comment = val.get("comment") + if params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None and comment != "" and not comment.startswith("# textdomain:"): + lOut.append(comment) + lOut.append(f"{localizedString}={translation}") + if params["break-long-lines"] and len(localizedString) > doublespace_threshold: + lOut.append("") + + unusedExist = False + if not params["truncate-unused"]: + 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" + comment_unused + "\n") + if params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "": + lOut.append("") + if comment != None: + lOut.append(comment) + lOut.append(f"{key}={translation}") + if params["break-long-lines"] and 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, existing_template[2], existing_template[3]) + 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() + + text = re.sub(pattern_concat, "", text) + + strings = [] + for s in pattern_lua_quoted.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed.findall(text): + strings.append(s) + + for s in strings: + found_bad = pattern_bad_luastring.search(s) + if found_bad: + print("SYNTAX ERROR: Unescaped '@' in Lua string: " + s) + continue + 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. +# Returns also header comments in the third return value. +def import_tr_file(tr_file): + dOut = {} + text = None + in_header = True + header_comments = None + textdomain = 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') + # "##### not used anymore #####" comment + if line == comment_unused: + # Always delete the 'not used anymore' comment. + # It will be re-added to the file if neccessary. + latest_comment_block = None + if header_comments != None: + in_header = False + continue + # Comment lines + elif line.startswith("#"): + # Source file comments: ##[ file.lua ]## + if line.startswith(symbol_source_prefix) and line.endswith(symbol_source_suffix): + # This line marks the end of header comments. + if params["print-source"]: + in_header = False + # Remove those comments; they may be added back automatically. + continue + + # Store first occurance of textdomain + # discard all subsequent textdomain lines + if line.startswith("# textdomain:"): + if textdomain == None: + textdomain = line + continue + elif in_header: + # Save header comments (normal comments at top of file) + if not header_comments: + header_comments = line + else: + header_comments = header_comments + "\n" + line + else: + # Save normal comments + if line.startswith("# textdomain:") and textdomain == None: + textdomain = line + elif 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 + in_header = False + + dOut[match.group(1)] = outval + return (dOut, text, header_comments, textdomain) + +# like os.walk but returns sorted filenames +def sorted_os_walk(folder): + tuples = [] + t = 0 + for root, dirs, files in os.walk(folder): + tuples.append( (root, dirs, files) ) + t = t + 1 + + tuples = sorted(tuples) + + paths_and_files = [] + f = 0 + + for tu in tuples: + root = tu[0] + dirs = tu[1] + files = tu[2] + files = sorted(files, key=str.lower) + for filename in files: + paths_and_files.append( (os.path.join(root, filename), filename) ) + f = f + 1 + return paths_and_files + +# 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 lists +# that can be used with the strings_to_text function. +def generate_template(folder, mod_name): + dOut = {} + paths_and_files = sorted_os_walk(folder) + for paf in paths_and_files: + fullpath_filename = paf[0] + filename = paf[1] + if fnmatch.fnmatch(filename, "*.lua"): + found = read_lua_file_strings(fullpath_filename) + if params["verbose"]: + print(f"{fullpath_filename}: {str(len(found))} translatable strings") + + for s in found: + sources = dOut.get(s, set()) + sources.add(os.path.relpath(fullpath_filename, start=folder)) + dOut[s] = sources + + if len(dOut) == 0: + return None + + # Convert source file set to list, sort it and add comment symbols. + # Needed because a set is unsorted and might result in unpredictable. + # output orders if any source string appears in multiple files. + for d in dOut: + sources = dOut.get(d, set()) + sources = sorted(list(sources), key=str.lower) + newSources = [] + for i in sources: + newSources.append(f"{symbol_source_prefix} {i} {symbol_source_suffix}") + dOut[d] = newSources + + 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, tr_import[2], tr_import[3]) + + if textOld and textOld != textNew: + print(f"{tr_file} has changed.") + if params["old-file"]: + 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: + 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(f"Unable to determine the mod name in folder {folder}. Missing 'name' field in mod.conf.", file=_stderr) + exit(1) + +# 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() and not f.name.startswith('.')] + 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() and not f.name.startswith('.')]: + update_folder(modfolder) + +main() diff --git a/mods/airutils/models/airutils_biofuel_distiller.b3d b/mods/airutils/models/airutils_biofuel_distiller.b3d new file mode 100644 index 00000000..f6d37aa7 Binary files /dev/null and b/mods/airutils/models/airutils_biofuel_distiller.b3d differ diff --git a/mods/airutils/models/airutils_seat_base.b3d b/mods/airutils/models/airutils_seat_base.b3d new file mode 100644 index 00000000..ccf1db3b Binary files /dev/null and b/mods/airutils/models/airutils_seat_base.b3d differ diff --git a/mods/airutils/models/airutils_wind.b3d b/mods/airutils/models/airutils_wind.b3d new file mode 100644 index 00000000..e2ad3ad7 Binary files /dev/null and b/mods/airutils/models/airutils_wind.b3d differ diff --git a/mods/airutils/models/papi.b3d b/mods/airutils/models/papi.b3d new file mode 100644 index 00000000..87619832 Binary files /dev/null and b/mods/airutils/models/papi.b3d differ diff --git a/mods/airutils/models/papi_emulation.blend b/mods/airutils/models/papi_emulation.blend new file mode 100644 index 00000000..e54b65b7 Binary files /dev/null and b/mods/airutils/models/papi_emulation.blend differ diff --git a/mods/airutils/models/papi_right.b3d b/mods/airutils/models/papi_right.b3d new file mode 100644 index 00000000..8c1a4a9e Binary files /dev/null and b/mods/airutils/models/papi_right.b3d differ diff --git a/mods/airutils/physics_lib.lua b/mods/airutils/physics_lib.lua new file mode 100644 index 00000000..3b8a126a --- /dev/null +++ b/mods/airutils/physics_lib.lua @@ -0,0 +1,182 @@ +airutils.gravity = -9.8 + +local abs = math.abs +local pi = math.pi +local floor = math.floor +local ceil = math.ceil +local random = math.random +local sqrt = math.sqrt +local max = math.max +local min = math.min +local tan = math.tan +local pow = math.pow + +local sign = function(x) + return (x<0) and -1 or 1 +end + +function airutils.rot_to_dir(rot) -- keep rot within <-pi/2,pi/2> + local dir = minetest.yaw_to_dir(rot.y) + dir.y = dir.y+tan(rot.x)*vector.length(dir) + return vector.normalize(dir) +end + +function airutils.dir_to_rot(v,rot) + rot = rot or {x=0,y=0,z=0} + return {x = (v.x==0 and v.y==0 and v.z==0) and rot.x or math.atan2(v.y,vector.length({x=v.x,y=0,z=v.z})), + y = (v.x==0 and v.z==0) and rot.y or minetest.dir_to_yaw(v), + z=rot.z} +end + +function airutils.pos_shift(pos,vec) -- vec components can be omitted e.g. vec={y=1} + vec.x=vec.x or 0 + vec.y=vec.y or 0 + vec.z=vec.z or 0 + return {x=pos.x+vec.x, + y=pos.y+vec.y, + z=pos.z+vec.z} +end + +function airutils.get_stand_pos(thing) -- thing can be luaentity or objectref. + local pos = {} + local colbox = {} + if type(thing) == 'table' then + pos = thing.object:get_pos() + if not thing.object:get_properties() then return false end + colbox = thing.object:get_properties().collisionbox + elseif type(thing) == 'userdata' then + pos = thing:get_pos() + if not thing:get_properties() then return false end + colbox = thing:get_properties().collisionbox + else + return false + end + return airutils.pos_shift(pos,{y=colbox[2]+0.01}), pos +end + +function airutils.get_node_pos(pos) + return { + x=floor(pos.x+0.5), + y=floor(pos.y+0.5), + z=floor(pos.z+0.5), + } +end + +function airutils.nodeatpos(pos) + if pos == nil then return end + local node = minetest.get_node_or_nil(pos) + if node then return minetest.registered_nodes[node.name] end +end + +function airutils.minmax(v,m) + return min(abs(v),m)*sign(v) +end + +function airutils.set_acceleration(thing,vec,limit) + limit = limit or 100 + if type(thing) == 'table' then thing=thing.object end + vec.x=airutils.minmax(vec.x,limit) + vec.y=airutils.minmax(vec.y,limit) + vec.z=airutils.minmax(vec.z,limit) + + thing:set_acceleration(vec) +end + +function airutils.actfunc(self, staticdata, dtime_s) + + self.logic = self.logic or self.brainfunc + self.physics = self.physics or airutils.physics + + self.lqueue = {} + self.hqueue = {} + self.nearby_objects = {} + self.nearby_players = {} + self.pos_history = {} + self.path_dir = 1 + self.time_total = 0 + self.water_drag = self.water_drag or 1 + + local sdata = minetest.deserialize(staticdata) + if sdata then + for k,v in pairs(sdata) do + self[k] = v + end + end + + if self.textures==nil then + local prop_tex = self.object:get_properties().textures + if prop_tex then self.textures=prop_tex end + end + + if not self.memory then -- this is the initial activation + self.memory = {} + + -- texture variation + if #self.textures > 1 then self.texture_no = random(#self.textures) end + end + + if self.timeout and ((self.timeout>0 and dtime_s > self.timeout and next(self.memory)==nil) or + (self.timeout<0 and dtime_s > abs(self.timeout))) then + self.object:remove() + end + + -- apply texture + if self.textures and self.texture_no then + local props = {} + props.textures = {self.textures[self.texture_no]} + self.object:set_properties(props) + end + +--hp + self.max_hp = self.max_hp or 10 + self.hp = self.hp or self.max_hp +--armor + if type(self.armor_groups) ~= 'table' then + self.armor_groups={} + end + self.armor_groups.immortal = 1 + self.object:set_armor_groups(self.armor_groups) + + self.buoyancy = self.buoyancy or 0 + self.oxygen = self.oxygen or self.lung_capacity + self.lastvelocity = {x=0,y=0,z=0} +end + +function airutils.get_box_height(self) + if type(self) == 'table' then self = self.object end + local colbox = self:get_properties().collisionbox + local height = 0.1 + if colbox then height = colbox[5]-colbox[2] end + + return height > 0 and height or 0.1 +end + +function airutils.stepfunc(self,dtime,colinfo) + self.dtime = min(dtime,0.2) + self.colinfo = colinfo + self.height = airutils.get_box_height(self) + +-- physics comes first + local vel = self.object:get_velocity() + + if colinfo then + self.isonground = colinfo.touching_ground + else + if self.lastvelocity.y==0 and vel.y==0 then + self.isonground = true + else + self.isonground = false + end + end + + self:physics() + + if self.logic then + if self.view_range then self:sensefunc() end + self:logic() + --execute_queues(self) + end + + self.lastvelocity = self.object:get_velocity() + self.time_total=self.time_total+self.dtime +end diff --git a/mods/airutils/pilot_skin_manager.lua b/mods/airutils/pilot_skin_manager.lua new file mode 100644 index 00000000..17f3a41b --- /dev/null +++ b/mods/airutils/pilot_skin_manager.lua @@ -0,0 +1,168 @@ +local S = airutils.S + +airutils.pilot_textures = {"pilot_clothes1.png","pilot_clothes2.png","pilot_clothes3.png","pilot_clothes4.png", + "pilot_novaskin_girl.png","pilot_novaskin_girl_steampunk.png","pilot_novaskin_girl_2.png","pilot_novaskin_girl_steampunk_2.png"} +local skinsdb_mod_path = minetest.get_modpath("skinsdb") + +minetest.register_chatcommand("au_uniform", { + func = function(name, param) + local player = minetest.get_player_by_name(name) + + if player then + if skinsdb_mod_path then + local skdb_skin = skins.get_player_skin(player) + if skdb_skin:get_meta("format") == "1.8" then + minetest.chat_send_player(name, S("Sorry, but uniform cannot be applied to format 1.8 skins.")) + return + end + end + airutils.uniform_formspec(name) + else + minetest.chat_send_player(name, S("Something isn't working...")) + end + end, +}) + +local set_player_textures = + minetest.get_modpath("player_api") and player_api.set_textures + or default.player_set_textures + +if skinsdb_mod_path then + -- Enhance apply_skin_to_player for all skins + local orig_apply_skin_to_player = skins.skin_class.apply_skin_to_player + function skins.skin_class:apply_skin_to_player(player) + local orig_texture = self:get_texture() + local player_meta = player:get_meta() + local pilot_skin = player_meta:get_string("pilot_skin") + if pilot_skin ~= "" then + if self:get_meta("format") == "1.8" then + -- format 1.8 is not suported + orig_apply_skin_to_player(self, player) + else + local orig_get_texture = self.get_texture + function self:get_texture() + return "[combine:64x32:0,0="..orig_texture.."^"..pilot_skin + end + orig_apply_skin_to_player(self, player) + self.get_texture = orig_get_texture + end + else + orig_apply_skin_to_player(self, player) + end + end +end + +function airutils.set_player_skin(player, skin) + if not player then return end + + -- use skinsdb enhancement + if skinsdb_mod_path then + local player_meta = player:get_meta() + player_meta:set_string("pilot_skin", skin) + local skdb_skin = skins.get_player_skin(player) + skdb_skin:apply_skin_to_player(player) + return + end + + -- manage byself + local player_properties = player:get_properties() + if not player_properties then return end + + local texture = player_properties.textures + local name = player:get_player_name() + if texture then + local player_meta = player:get_meta() + if skin then + --get current texture + texture = texture[1] + + --backup current texture + local backup = player_meta:get_string("backup") + if backup == nil or backup == "" then + --player:set_attribute(backup, texture) --texture backup + player_meta:set_string("backup",texture) + else + texture = backup + end + + --combine the texture + texture = texture.."^"..skin + + --sets the combined texture + if texture ~= nil and texture ~= "" then + set_player_textures(player, {texture}) + player_meta:set_string("curr_skin",texture) + --player:set_attribute(curr_skin, texture) + end + else + --remove texture + local old_texture = player_meta:get_string("backup") + if minetest.global_exists("set_skin") then --checking if set_skin is available + if player:get_attribute("set_skin:player_skin") ~= nil and player:get_attribute("set_skin:player_skin") ~= "" then + old_texture = player:get_attribute("set_skin:player_skin") + end + elseif minetest.global_exists("wardrobe") then --checking if wardrobe is available + if wardrobe.playerSkins then + if wardrobe.playerSkins[name] ~= nil then + old_texture = wardrobe.playerSkins[name] + end + end + end + --minetest.chat_send_all(dump(old_texture)) + if old_texture ~= nil and old_texture ~= "" then + set_player_textures(player, { old_texture }) + end + player_meta:set_string("backup","") + player_meta:set_string("curr_skin","") + + --player:set_attribute(backup, nil) + --player:set_attribute(curr_skin, nil) + end + end +end + +function airutils.uniform_formspec(name) + local basic_form = table.concat({ + "formspec_version[5]", + "size[5,2.9]", + }, "") + + --minetest.chat_send_all(dump(airutils.pilot_textures)) + + local textures = "" + if airutils.pilot_textures then + for k, v in pairs( airutils.pilot_textures ) do + textures = textures .. v .. "," + end + + basic_form = basic_form.."dropdown[0.5,0.5;4,0.8;textures;".. textures ..";0;false]" + basic_form = basic_form.."button[0.5,1.6;4,0.8;set_texture;" .. S("Set Player Texture") .. "]" + + minetest.show_formspec(name, "airutils:change", basic_form) + else + minetest.chat_send_player(name, S("The isn't activated as secure. Aborting")) + end +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "airutils:change" then + local name = player:get_player_name() + if fields.textures or fields.set_texture then + airutils.set_player_skin(player, fields.textures) + end + minetest.close_formspec(name, "airutils:change") + end +end) + +minetest.register_on_joinplayer(function(player) + local player_meta = player:get_meta() + local skin = player_meta:get_string("curr_skin") + --minetest.chat_send_all(">>>"..skin) + + if skin and skin ~= "" and skin ~= nil then + -- setting player skin on connect has no effect, so delay skin change + minetest.after(3, function(player1, skin1) + airutils.set_player_skin(player1, skin1) + end, player, skin) + end +end) diff --git a/mods/airutils/screenshot.png b/mods/airutils/screenshot.png new file mode 100644 index 00000000..f2dca347 Binary files /dev/null and b/mods/airutils/screenshot.png differ diff --git a/mods/airutils/settingtypes.txt b/mods/airutils/settingtypes.txt new file mode 100644 index 00000000..3103b834 --- /dev/null +++ b/mods/airutils/settingtypes.txt @@ -0,0 +1,24 @@ +# all settings for client menu or in minetest.conf on servers + + +# protect airplanes from player damage in protected areas +airutils_protect_in_areas (protect in area) bool true + +# disable PAPI (Precision Approach Path Indicator) +airutils_disable_papi (disable PAPI) bool false + +# disable tug tool (reposition of not owning planes) +airutils_disable_tug (disable tug tool) bool false + +# disable repair tool for planes +airutils_disable_repair (planes cannot be repaired) bool false + +# enable debug od activation and deactivation of the vehicle entity +airutils_debug_log (log of entity activation) bool false + +# disable the usage of signs_api by the vehicles +airutils_disable_signs_api (no names writen on vehicles surface) bool false + +# enable water particles effect +airutils_enable_water_particles (enable particles on water) bool false + diff --git a/mods/airutils/sounds/airutils_beep.ogg b/mods/airutils/sounds/airutils_beep.ogg new file mode 100644 index 00000000..645cbce3 Binary files /dev/null and b/mods/airutils/sounds/airutils_beep.ogg differ diff --git a/mods/airutils/sounds/airutils_collision.ogg b/mods/airutils/sounds/airutils_collision.ogg new file mode 100644 index 00000000..b15fbb1e Binary files /dev/null and b/mods/airutils/sounds/airutils_collision.ogg differ diff --git a/mods/airutils/sounds/airutils_engine.ogg b/mods/airutils/sounds/airutils_engine.ogg new file mode 100644 index 00000000..a10b5521 Binary files /dev/null and b/mods/airutils/sounds/airutils_engine.ogg differ diff --git a/mods/airutils/sounds/airutils_explode.ogg b/mods/airutils/sounds/airutils_explode.ogg new file mode 100644 index 00000000..e00a16c1 Binary files /dev/null and b/mods/airutils/sounds/airutils_explode.ogg differ diff --git a/mods/airutils/sounds/airutils_heli_snd.ogg b/mods/airutils/sounds/airutils_heli_snd.ogg new file mode 100644 index 00000000..00282b97 Binary files /dev/null and b/mods/airutils/sounds/airutils_heli_snd.ogg differ diff --git a/mods/airutils/sounds/airutils_heli_snd.txt b/mods/airutils/sounds/airutils_heli_snd.txt new file mode 100644 index 00000000..039346b8 --- /dev/null +++ b/mods/airutils/sounds/airutils_heli_snd.txt @@ -0,0 +1,5 @@ +airutils_heli_snd.ogg +remixed from +https://freesound.org/people/Carlfnf/sounds/700861/ +Author Carlfnf +Licence CC0 diff --git a/mods/airutils/sounds/airutils_touch.ogg b/mods/airutils/sounds/airutils_touch.ogg new file mode 100644 index 00000000..90d35c42 Binary files /dev/null and b/mods/airutils/sounds/airutils_touch.ogg differ diff --git a/mods/airutils/sounds/airutils_touch_water.ogg b/mods/airutils/sounds/airutils_touch_water.ogg new file mode 100644 index 00000000..a3bb33f0 Binary files /dev/null and b/mods/airutils/sounds/airutils_touch_water.ogg differ diff --git a/mods/airutils/text.lua b/mods/airutils/text.lua new file mode 100644 index 00000000..c72cebd7 --- /dev/null +++ b/mods/airutils/text.lua @@ -0,0 +1,280 @@ +--[[ +License for code: LGPL 3.0 +see at: https://www.gnu.org/licenses/lgpl-3.0.txt + +This code was adapted from signs_lib from VanessaE +the original lib can be found at: https://content.minetest.net/packages/mt-mods/signs_lib/ +]]-- + +-- CONSTANTS + +-- Path to the textures. +local TP = signs_lib.path .. "/textures" +-- Font file formatter +local CHAR_FILE = "%s_%02x.png" +-- Fonts path +local CHAR_PATH = TP .. "/" .. CHAR_FILE +local max_lenght = 20 + +-- Initialize character texture cache +local ctexcache = {} + +local function fill_line(x, y, w, c, font_size, colorbgw) + c = c or "0" + local tex = { } + for xx = 0, math.max(0, w), colorbgw do + table.insert(tex, (":%d,%d=signs_lib_color_"..font_size.."px_%s.png"):format(x + xx, y, c)) + end + return table.concat(tex) +end + +local function clamp_characters(text, max_lenght) + text = text or "" + max_lenght = max_lenght or 20 + local control_chars = {"##","#0","#1","#2","#3","#4","#5","#6","#7","#8","#9","#a","#b","#c","#d","#e","#f"} + local control_order = {} + local new_string = text + + --first creates a memory of each control code + for i = 1, #new_string do + local c = new_string:sub(i,i+1) + for i, item in pairs(control_chars) do + if c == item then + table.insert(control_order, item) + break + end + end + end + + --create control spaces (the order was saved in "control_order" + local control_char = "\001" + for i, item in pairs(control_chars) do + new_string = string.gsub(new_string, item, control_char) + end + + --now make another string counting it outside and breaking when reachs 20 + local count = 0 + local curr_index = 0 + local c = "" + for i = 1, #new_string, 1 do + c = string.sub(new_string,i,i) + if c ~= control_char then + count = count + 1 + end + curr_index = i + if count == max_lenght then break end + end + local cutstring = string.sub(new_string,1,curr_index) + + --now reconstruct the string + local outputstring = "" + local control_order_curr_intex = 0 + for i = 1, #cutstring, 1 do + c = string.sub(cutstring,i,i) + if c ~= control_char then + outputstring = outputstring .. (c or "") + else + control_order_curr_intex = control_order_curr_intex + 1 + outputstring = outputstring .. (control_order[control_order_curr_intex] or "") + end + end + + return outputstring, count +end + +-- check if a file does exist +-- to avoid reopening file after checking again +-- pass TRUE as second argument +local function file_exists(name, return_handle, mode) + mode = mode or "r"; + local f = io.open(name, mode) + if f ~= nil then + if (return_handle) then + return f + end + io.close(f) + return true + else + return false + end +end + +-- make char texture file name +-- if texture file does not exist use fallback texture instead +local function char_tex(font_name, ch) + if ctexcache[font_name..ch] then + return ctexcache[font_name..ch], true + else + local c = ch:byte() + local exists, tex = file_exists(CHAR_PATH:format(font_name, c)) + if exists and c ~= 14 then + tex = CHAR_FILE:format(font_name, c) + else + tex = CHAR_FILE:format(font_name, 0x0) + end + ctexcache[font_name..ch] = tex + return tex, exists + end +end + +local function make_text_texture(text, default_color, line_width, line_height, cwidth_tab, font_size, colorbgw) + local split = signs_lib.split_lines_and_words + local width = 0 + local maxw = 0 + local font_name = "signs_lib_font_"..font_size.."px" + local text_ansi = signs_lib.Utf8ToAnsi(text) + local text_splited = split(text_ansi)[1] + + local words = { } + default_color = default_color or 0 + + local cur_color = tonumber(default_color, 16) + + -- We check which chars are available here. + for word_i, word in ipairs(text_splited) do + local chars = { } + local ch_offs = 0 + word = string.gsub(word, "%^[12345678abcdefgh]", { + ["^1"] = string.char(0x81), + ["^2"] = string.char(0x82), + ["^3"] = string.char(0x83), + ["^4"] = string.char(0x84), + ["^5"] = string.char(0x85), + ["^6"] = string.char(0x86), + ["^7"] = string.char(0x87), + ["^8"] = string.char(0x88), + ["^a"] = string.char(0x8a), + ["^b"] = string.char(0x8b), + ["^c"] = string.char(0x8c), + ["^d"] = string.char(0x8d), + ["^e"] = string.char(0x8e), + ["^f"] = string.char(0x8f), + ["^g"] = string.char(0x90), + ["^h"] = string.char(0x91) + }) + local word_l = #word + local i = 1 + while i <= word_l do + local c = word:sub(i, i) + if c == "#" then + local cc = tonumber(word:sub(i+1, i+1), 16) + if cc then + i = i + 1 + cur_color = cc + end + else + local w = cwidth_tab[c] + if w then + width = width + w + 1 + if width >= (line_width - cwidth_tab[" "]) then + width = 0 + else + maxw = math.max(width, maxw) + end + local max_input_chars = max_lenght + if #chars < max_input_chars then + table.insert(chars, { + off = ch_offs, + tex = char_tex(font_name, c), + col = ("%X"):format(cur_color), + }) + end + ch_offs = ch_offs + w + end + end + i = i + 1 + end + width = width + cwidth_tab[" "] + 1 + maxw = math.max(width, maxw) + table.insert(words, { chars=chars, w=ch_offs }) + end + + -- Okay, we actually build the "line texture" here. + + local texture = { } + + local start_xpos = math.floor((line_width - maxw) / 2) + + local xpos = start_xpos + local ypos = line_height + if line_height == signs_lib.lineheight32 then ypos = line_height/4 end + + cur_color = nil + + for word_i, word in ipairs(words) do + local xoffs = (xpos - start_xpos) + if (xoffs > 0) and ((xoffs + word.w) > maxw) then + table.insert(texture, fill_line(xpos, ypos, maxw, "n", font_size, colorbgw)) + xpos = start_xpos + ypos = ypos + line_height + table.insert(texture, fill_line(xpos, ypos, maxw, cur_color, font_size, colorbgw)) + end + for ch_i, ch in ipairs(word.chars) do + if ch.col ~= cur_color then + cur_color = ch.col + table.insert(texture, fill_line(xpos + ch.off, ypos, maxw, cur_color, font_size, colorbgw)) + end + table.insert(texture, (":%d,%d=%s"):format(xpos + ch.off, ypos, ch.tex)) + end + table.insert( + texture, + (":%d,%d="):format(xpos + word.w, ypos) .. char_tex(font_name, " ") + ) + xpos = xpos + word.w + cwidth_tab[" "] + if xpos >= (line_width + cwidth_tab[" "]) then break end + end + + table.insert(texture, fill_line(xpos, ypos, maxw, "n", font_size, colorbgw)) + table.insert(texture, fill_line(start_xpos, ypos + line_height, maxw, "n", font_size, colorbgw)) + + return table.concat(texture) +end + +function airutils.convert_text_to_texture(text, default_color, horizontal_aligment) + text = text or "" + default_color = default_color or 0 + horizontal_aligment = horizontal_aligment or 3 + if not signs_lib then return "" end + local font_size = 16 + local line_width = 1 + local line_height = 1 + local char_width = 1 + local colorbgw = "" + local chars_per_line = 21 + local widemult = 0.5 + local count = 0 + --text = string.sub(text,1,max_lenght) + text, count = clamp_characters(text, max_lenght) + + if count <= 10 then + if signs_lib.avgwidth32 then + widemult = 0.75 + font_size = 32 + chars_per_line = 10 + line_width = math.floor(signs_lib.avgwidth32 * chars_per_line) * (horizontal_aligment * widemult) + line_height = signs_lib.lineheight32 + char_width = signs_lib.charwidth32 + colorbgw = signs_lib.colorbgw32 + else + return "" + end + else + if signs_lib.avgwidth16 then + widemult = 0.5 + font_size = 16 + chars_per_line = 21 + line_width = math.floor(signs_lib.avgwidth16 * chars_per_line) * (horizontal_aligment * widemult) + line_height = signs_lib.lineheight16 + char_width = signs_lib.charwidth16 + colorbgw = signs_lib.colorbgw16 + else + return "" + end + end + + local texture = { ("[combine:%dx%d"):format(line_width, line_height) } + local linetex = make_text_texture(text, default_color, line_width, line_height, char_width, font_size, colorbgw) + table.insert(texture, linetex) + table.insert(texture, "^[makealpha:0,0,0") + return table.concat(texture, "") +end diff --git a/mods/airutils/texture_management.lua b/mods/airutils/texture_management.lua new file mode 100644 index 00000000..491eae44 --- /dev/null +++ b/mods/airutils/texture_management.lua @@ -0,0 +1,137 @@ +-- Function to extract textures from a node definition +local function getTexturesFromNode(node) + local textures = {} + + if node.tiles then + if type(node.tiles) == "string" then + table.insert(textures, node.tiles) + elseif type(node.tiles) == "table" then + for _, tile in pairs(node.tiles) do + if type(tile) == "string" then + table.insert(textures, tile) + end + end + end + end + + return textures +end + +-- Function to extract textures from an entity definition +local function getTexturesFromEntity(entityDef) + local textures = {} + + if entityDef.textures then + if type(entityDef.textures) == "string" then + table.insert(textures, entityDef.textures) + elseif type(entityDef.textures) == "table" then + for _, texture in pairs(entityDef.textures) do + if type(texture) == "string" then + table.insert(textures, texture) + end + end + end + end + + return textures +end + +-- Function to extract textures from an item definition +local function getTexturesFromItem(itemDef) + local textures = {} + + if itemDef.inventory_image then + table.insert(textures, itemDef.inventory_image) + end + + return textures +end + +--function to remove duplicates and invalid for textlist +local function filter_texture_names(list_to_test) + local hash = {} + local result = {} + for _,v in ipairs(list_to_test) do + if not string.find(v, ",") then + if (not hash[v]) then + result[#result+1] = v + hash[v] = true + end + end + end + return result +end + +-- Function to list all loaded textures +local function listLoadedTextures() + local loadedTextures = {} + + --nodes + for name, node in pairs(minetest.registered_nodes) do + local textures = getTexturesFromNode(node) + + for _, texture in pairs(textures) do + table.insert(loadedTextures, texture) + end + end + airutils.all_game_textures = filter_texture_names(loadedTextures) + + --entities + loadedTextures = {} + for name, entityDef in pairs(minetest.registered_entities) do + local textures = getTexturesFromEntity(entityDef) + + for _, texture in pairs(textures) do + table.insert(loadedTextures, texture) + end + end + airutils.all_entities_textures = filter_texture_names(loadedTextures) + + --items + loadedTextures = {} + for name, itemDef in pairs(minetest.registered_items) do + local textures = getTexturesFromItem(itemDef) + + for _, texture in pairs(textures) do + table.insert(loadedTextures, texture) + end + end + + if airutils.is_minetest then + table.insert(loadedTextures,'bubble.png') + table.insert(loadedTextures,'heart.png') + end + airutils.all_items_textures = filter_texture_names(loadedTextures) +end + +-- Function to check if a texture is in the list of loaded textures +function airutils.isTextureLoaded(textureToFind) + if not airutils.all_game_textures then + listLoadedTextures() + end + local textures = airutils.all_game_textures + + for _, texture in pairs(textures) do + if texture == textureToFind then + return true + end + end + + textures = airutils.all_entities_textures + + for _, texture in pairs(textures) do + if texture == textureToFind then + return true + end + end + + textures = airutils.all_items_textures + + for _, texture in pairs(textures) do + if texture == textureToFind then + return true + end + end + + return false +end diff --git a/mods/airutils/textures/airutils_alpha.png b/mods/airutils/textures/airutils_alpha.png new file mode 100644 index 00000000..1397ecc4 Binary files /dev/null and b/mods/airutils/textures/airutils_alpha.png differ diff --git a/mods/airutils/textures/airutils_altimeter_gauge.png b/mods/airutils/textures/airutils_altimeter_gauge.png new file mode 100644 index 00000000..aa4d8e77 Binary files /dev/null and b/mods/airutils/textures/airutils_altimeter_gauge.png differ diff --git a/mods/airutils/textures/airutils_aluminum.png b/mods/airutils/textures/airutils_aluminum.png new file mode 100644 index 00000000..df36eb6c Binary files /dev/null and b/mods/airutils/textures/airutils_aluminum.png differ diff --git a/mods/airutils/textures/airutils_biofuel_inv.png b/mods/airutils/textures/airutils_biofuel_inv.png new file mode 100644 index 00000000..1f2965fa Binary files /dev/null and b/mods/airutils/textures/airutils_biofuel_inv.png differ diff --git a/mods/airutils/textures/airutils_black.png b/mods/airutils/textures/airutils_black.png new file mode 100644 index 00000000..4c432225 Binary files /dev/null and b/mods/airutils/textures/airutils_black.png differ diff --git a/mods/airutils/textures/airutils_black2.png b/mods/airutils/textures/airutils_black2.png new file mode 100644 index 00000000..01c787e7 Binary files /dev/null and b/mods/airutils/textures/airutils_black2.png differ diff --git a/mods/airutils/textures/airutils_blue.png b/mods/airutils/textures/airutils_blue.png new file mode 100644 index 00000000..cfa137cd Binary files /dev/null and b/mods/airutils/textures/airutils_blue.png differ diff --git a/mods/airutils/textures/airutils_boom.png b/mods/airutils/textures/airutils_boom.png new file mode 100644 index 00000000..0e26bd4a Binary files /dev/null and b/mods/airutils/textures/airutils_boom.png differ diff --git a/mods/airutils/textures/airutils_brown.png b/mods/airutils/textures/airutils_brown.png new file mode 100644 index 00000000..47e88ab5 Binary files /dev/null and b/mods/airutils/textures/airutils_brown.png differ diff --git a/mods/airutils/textures/airutils_burned_metal.png b/mods/airutils/textures/airutils_burned_metal.png new file mode 100644 index 00000000..3d9e5f6f Binary files /dev/null and b/mods/airutils/textures/airutils_burned_metal.png differ diff --git a/mods/airutils/textures/airutils_climber_gauge.png b/mods/airutils/textures/airutils_climber_gauge.png new file mode 100644 index 00000000..b621b8d8 Binary files /dev/null and b/mods/airutils/textures/airutils_climber_gauge.png differ diff --git a/mods/airutils/textures/airutils_copper.png b/mods/airutils/textures/airutils_copper.png new file mode 100644 index 00000000..883b506d Binary files /dev/null and b/mods/airutils/textures/airutils_copper.png differ diff --git a/mods/airutils/textures/airutils_form_bg.png b/mods/airutils/textures/airutils_form_bg.png new file mode 100644 index 00000000..76627854 Binary files /dev/null and b/mods/airutils/textures/airutils_form_bg.png differ diff --git a/mods/airutils/textures/airutils_fuel_gauge.png b/mods/airutils/textures/airutils_fuel_gauge.png new file mode 100644 index 00000000..7242f8f5 Binary files /dev/null and b/mods/airutils/textures/airutils_fuel_gauge.png differ diff --git a/mods/airutils/textures/airutils_gauge_bg.png b/mods/airutils/textures/airutils_gauge_bg.png new file mode 100644 index 00000000..0daf3a96 Binary files /dev/null and b/mods/airutils/textures/airutils_gauge_bg.png differ diff --git a/mods/airutils/textures/airutils_green.png b/mods/airutils/textures/airutils_green.png new file mode 100644 index 00000000..2ebd3d79 Binary files /dev/null and b/mods/airutils/textures/airutils_green.png differ diff --git a/mods/airutils/textures/airutils_grey.png b/mods/airutils/textures/airutils_grey.png new file mode 100644 index 00000000..2c322b25 Binary files /dev/null and b/mods/airutils/textures/airutils_grey.png differ diff --git a/mods/airutils/textures/airutils_hud_panel.png b/mods/airutils/textures/airutils_hud_panel.png new file mode 100644 index 00000000..419307de Binary files /dev/null and b/mods/airutils/textures/airutils_hud_panel.png differ diff --git a/mods/airutils/textures/airutils_ind_box.png b/mods/airutils/textures/airutils_ind_box.png new file mode 100644 index 00000000..af72d159 Binary files /dev/null and b/mods/airutils/textures/airutils_ind_box.png differ diff --git a/mods/airutils/textures/airutils_ind_box_2.png b/mods/airutils/textures/airutils_ind_box_2.png new file mode 100644 index 00000000..caa5b489 Binary files /dev/null and b/mods/airutils/textures/airutils_ind_box_2.png differ diff --git a/mods/airutils/textures/airutils_light.png b/mods/airutils/textures/airutils_light.png new file mode 100644 index 00000000..7fd58b5e Binary files /dev/null and b/mods/airutils/textures/airutils_light.png differ diff --git a/mods/airutils/textures/airutils_metal.png b/mods/airutils/textures/airutils_metal.png new file mode 100644 index 00000000..61ad05c8 Binary files /dev/null and b/mods/airutils/textures/airutils_metal.png differ diff --git a/mods/airutils/textures/airutils_name_canvas.png b/mods/airutils/textures/airutils_name_canvas.png new file mode 100644 index 00000000..65b13592 Binary files /dev/null and b/mods/airutils/textures/airutils_name_canvas.png differ diff --git a/mods/airutils/textures/airutils_painting.png b/mods/airutils/textures/airutils_painting.png new file mode 100644 index 00000000..99d41c95 Binary files /dev/null and b/mods/airutils/textures/airutils_painting.png differ diff --git a/mods/airutils/textures/airutils_painting_2.png b/mods/airutils/textures/airutils_painting_2.png new file mode 100644 index 00000000..99d41c95 Binary files /dev/null and b/mods/airutils/textures/airutils_painting_2.png differ diff --git a/mods/airutils/textures/airutils_red.png b/mods/airutils/textures/airutils_red.png new file mode 100644 index 00000000..d1986c07 Binary files /dev/null and b/mods/airutils/textures/airutils_red.png differ diff --git a/mods/airutils/textures/airutils_repair_tool.png b/mods/airutils/textures/airutils_repair_tool.png new file mode 100644 index 00000000..f7692e6a Binary files /dev/null and b/mods/airutils/textures/airutils_repair_tool.png differ diff --git a/mods/airutils/textures/airutils_rpm_gauge.png b/mods/airutils/textures/airutils_rpm_gauge.png new file mode 100644 index 00000000..9b356058 Binary files /dev/null and b/mods/airutils/textures/airutils_rpm_gauge.png differ diff --git a/mods/airutils/textures/airutils_smoke.png b/mods/airutils/textures/airutils_smoke.png new file mode 100644 index 00000000..15e31d52 Binary files /dev/null and b/mods/airutils/textures/airutils_smoke.png differ diff --git a/mods/airutils/textures/airutils_speed_gauge.png b/mods/airutils/textures/airutils_speed_gauge.png new file mode 100644 index 00000000..244d161b Binary files /dev/null and b/mods/airutils/textures/airutils_speed_gauge.png differ diff --git a/mods/airutils/textures/airutils_splash.png b/mods/airutils/textures/airutils_splash.png new file mode 100644 index 00000000..5760a7f1 Binary files /dev/null and b/mods/airutils/textures/airutils_splash.png differ diff --git a/mods/airutils/textures/airutils_transparent.png b/mods/airutils/textures/airutils_transparent.png new file mode 100644 index 00000000..1397ecc4 Binary files /dev/null and b/mods/airutils/textures/airutils_transparent.png differ diff --git a/mods/airutils/textures/airutils_tug.png b/mods/airutils/textures/airutils_tug.png new file mode 100644 index 00000000..fbc7a0d6 Binary files /dev/null and b/mods/airutils/textures/airutils_tug.png differ diff --git a/mods/airutils/textures/airutils_u_black.png b/mods/airutils/textures/airutils_u_black.png new file mode 100644 index 00000000..22930fbe Binary files /dev/null and b/mods/airutils/textures/airutils_u_black.png differ diff --git a/mods/airutils/textures/airutils_white.png b/mods/airutils/textures/airutils_white.png new file mode 100644 index 00000000..14334922 Binary files /dev/null and b/mods/airutils/textures/airutils_white.png differ diff --git a/mods/airutils/textures/gauge_bg.xcf b/mods/airutils/textures/gauge_bg.xcf new file mode 100644 index 00000000..6079d9c7 Binary files /dev/null and b/mods/airutils/textures/gauge_bg.xcf differ diff --git a/mods/airutils/textures/papi.png b/mods/airutils/textures/papi.png new file mode 100644 index 00000000..c6acb1c3 Binary files /dev/null and b/mods/airutils/textures/papi.png differ diff --git a/mods/airutils/textures/pilot_clothes1.png b/mods/airutils/textures/pilot_clothes1.png new file mode 100644 index 00000000..e8ce4087 Binary files /dev/null and b/mods/airutils/textures/pilot_clothes1.png differ diff --git a/mods/airutils/textures/pilot_clothes2.png b/mods/airutils/textures/pilot_clothes2.png new file mode 100644 index 00000000..1641881d Binary files /dev/null and b/mods/airutils/textures/pilot_clothes2.png differ diff --git a/mods/airutils/textures/pilot_clothes3.png b/mods/airutils/textures/pilot_clothes3.png new file mode 100644 index 00000000..fed82bf8 Binary files /dev/null and b/mods/airutils/textures/pilot_clothes3.png differ diff --git a/mods/airutils/textures/pilot_clothes4.png b/mods/airutils/textures/pilot_clothes4.png new file mode 100644 index 00000000..ca415d5f Binary files /dev/null and b/mods/airutils/textures/pilot_clothes4.png differ diff --git a/mods/airutils/textures/pilot_novaskin_girl.png b/mods/airutils/textures/pilot_novaskin_girl.png new file mode 100644 index 00000000..d69c1205 Binary files /dev/null and b/mods/airutils/textures/pilot_novaskin_girl.png differ diff --git a/mods/airutils/textures/pilot_novaskin_girl_2.png b/mods/airutils/textures/pilot_novaskin_girl_2.png new file mode 100644 index 00000000..669f3b96 Binary files /dev/null and b/mods/airutils/textures/pilot_novaskin_girl_2.png differ diff --git a/mods/airutils/textures/pilot_novaskin_girl_steampunk.png b/mods/airutils/textures/pilot_novaskin_girl_steampunk.png new file mode 100644 index 00000000..170e737f Binary files /dev/null and b/mods/airutils/textures/pilot_novaskin_girl_steampunk.png differ diff --git a/mods/airutils/textures/pilot_novaskin_girl_steampunk_2.png b/mods/airutils/textures/pilot_novaskin_girl_steampunk_2.png new file mode 100644 index 00000000..12c9d086 Binary files /dev/null and b/mods/airutils/textures/pilot_novaskin_girl_steampunk_2.png differ diff --git a/mods/airutils/textures/pilot_phones.png b/mods/airutils/textures/pilot_phones.png new file mode 100644 index 00000000..6617bd6b Binary files /dev/null and b/mods/airutils/textures/pilot_phones.png differ diff --git a/mods/airutils/uuid_manager.lua b/mods/airutils/uuid_manager.lua new file mode 100644 index 00000000..6a7febc8 --- /dev/null +++ b/mods/airutils/uuid_manager.lua @@ -0,0 +1,21 @@ +--function from yl_scheduler +--[[ +Copyright (c) 2024 AliasAlreadyTaken + +MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]]-- + +local function generate_uuid() + local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' + return string.gsub(template, '[xy]', function(c) + local v = (c == 'x') and math.random(0, 15) or math.random(8, 11) + return string.format('%x', v) + end) +end + +--exporter for airutils +function airutils.generate_uuid() return generate_uuid() end + diff --git a/mods/airutils/water_splash.lua b/mods/airutils/water_splash.lua new file mode 100644 index 00000000..3972b2ef --- /dev/null +++ b/mods/airutils/water_splash.lua @@ -0,0 +1,52 @@ +local function calculateVelocity(magnitude, angle) + -- Calcula os componentes do vetor usando ângulo polar + -- Supondo que o ângulo é dado no plano XY, com z = 0 + local velocity = { + x = magnitude * math.cos(angle), + y = 0, -- Se a velocidade não tem componente z + z = magnitude * math.sin(angle), + } + + return velocity +end + +local function water_particle(pos, accell) + if airutils.splash_texture == nil then return end + if airutils.splash_texture == "" then return end + + minetest.add_particle({ + pos = pos, + velocity = {x = 0, y = 0, z = 0}, + acceleration = accell, --{x = 0, y = 0, z = 0}, + expirationtime = 2.0, + size = 4.8, + collisiondetection = false, + collision_removal = false, + vertical = false, + texture = airutils.splash_texture, + }) +end + +function airutils.add_splash(pos, yaw, x_pos) + local direction = yaw + + local spl_pos = vector.new(pos) + --water_particle(spl_pos, {x=0,y=0,z=0}) + + --right + local move = x_pos/10 + spl_pos.x = spl_pos.x + move * math.cos(direction) + spl_pos.z = spl_pos.z + move * math.sin(direction) + + local velocity = calculateVelocity(0.2, yaw) + water_particle(spl_pos, velocity) + + --left + direction = direction - math.rad(180) + spl_pos = vector.new(pos) + spl_pos.x = spl_pos.x + move * math.cos(direction) + spl_pos.z = spl_pos.z + move * math.sin(direction) + + velocity = calculateVelocity(0.2, yaw - math.rad(180)) + water_particle(spl_pos, velocity) +end diff --git a/mods/airutils/wind.lua b/mods/airutils/wind.lua new file mode 100644 index 00000000..48bdcc42 --- /dev/null +++ b/mods/airutils/wind.lua @@ -0,0 +1,60 @@ +--[[ +MIT License + +Copyright (c) 2019 TheTermos, TestificateMods + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]]-- + +local get_wind + +if minetest.get_modpath("climate_api") then + get_wind = function(pos, multiplier) + multiplier = multiplier or 0.1 + local wind = climate_api.environment.get_wind({x=0,y=0,z=0}) + return vector.multiply(wind, multiplier) + end + +else + local yaw = math.random()*math.pi*2-math.pi + local speed = math.random()*4.0 + airutils.wind={} + airutils.wind.wind = vector.multiply(minetest.yaw_to_dir(yaw),speed) + airutils.wind.timer = 0 + airutils.wind.ttime = math.random()*5*60+1*60 + + get_wind = function(pos, multiplier) + local retVal = vector.multiply(airutils.wind.wind, multiplier) + return retVal + end + + minetest.register_globalstep( + function(dtime) + airutils.wind.timer=airutils.wind.timer+dtime + if airutils.wind.timer >= airutils.wind.ttime then + local yaw = minetest.dir_to_yaw(airutils.wind.wind) + yaw = yaw+math.random()-0.5 + local speed = math.random()*4.0 + airutils.wind.wind = vector.multiply(minetest.yaw_to_dir(yaw),speed) + airutils.wind.ttime = airutils.wind.timer+math.random()*5*60+1*60 + end + end) +end + +return get_wind diff --git a/mods/caverealms/README.md b/mods/caverealms/README.md index 4ab76b83..f33fb754 100644 --- a/mods/caverealms/README.md +++ b/mods/caverealms/README.md @@ -1,9 +1,25 @@ -# caverealms-lite +# caverealms-lite-plus -Based on the original minetest-caverealms mod (https://github.com/HeroOfTheWinds/minetest-caverealms/). +Based on Ezhh's cavereals-lite (https://github.com/Ezhh/caverealms_lite/), itself based on the original minetest-caverealms mod (https://github.com/HeroOfTheWinds/minetest-caverealms/). Adds underground realms to minetest. +This mod may be used as a drop-in replacement for caverealms-lite. Newly generated blocks will show a sharp lurch in the biome type. + + +## Changes from caverealms-lite + +* Different biome algorithm based on independent Evil and Wonder noises, similar to heat and humidity above ground. +* Biomes are larger. +* Every biome has some sort of consistent light source +* New biome type: geothermal hotsprings. +* A few more node types similar to existing ones. +* Various bug fixes. +* Various new bugs ;) + + +## Changes by Ezhh from caverealms to caverealms-lite + This caverealms fork provides all the biomes and decorations from the original caverealms, with several additions and without the overhead of generating caves. This lowers the server resources the mod requires, for example CPU and RAM. This also removes the large lava spills created by the original caverealms. It is specifically written to work with the mgvalleys mapgen, but will work using other mapgens as well. The mapgen used will determine the shape and size of individual caves. Mapgens that generate only smaller caves may be less suitable for use with this fork than mgvalleys. @@ -17,8 +33,9 @@ Source code: FreeBSD License (Simplified) The original caverealms was licensed as WTFPL. Contributors: -- Zeno, Shara RedCat - This rewrite -- HeroOfTheWinds, Zeno - Original mod +- yzziizzy - current version +- Zeno, Shara RedCat - caverealms-lite +- HeroOfTheWinds, Zeno - Original caverealms mod ## Recommended Additions diff --git a/mods/caverealms/config.lua b/mods/caverealms/config.lua index 265ccb00..fe950a59 100644 --- a/mods/caverealms/config.lua +++ b/mods/caverealms/config.lua @@ -20,19 +20,20 @@ local function setting(stype, name, default) end --generation settings -setting("number", "ymin", -10000) --bottom realm limit +setting("number", "ymin", -15000) --bottom realm limit setting("number", "ymax", -4096) --top realm limit setting("number", "tcave", 0.75) --cave threshold --decoration chances -setting("number", "stagcha", 0.03) --chance of stalagmites (was 0.003) -setting("number", "stalcha", 0.03) --chance of stalactites(was 0.003) +setting("number", "stagcha", 0.01) --chance of stalagmites 0.003 +setting("number", "stalcha", 0.01) --chance of stalactites 0.003 -setting("number", "h_lag", 8) --max height for stalagmites -setting("number", "h_lac", 8) --...stalactites -setting("number", "crystal", 0.02) --chance of glow crystal formations (was 0.0002) -setting("number", "h_cry", 8) --max height of glow crystals -setting("number", "h_clac", 8) --max height of glow crystal stalactites +setting("number", "h_lag", 15) --max height for stalagmites +setting("number", "h_lac", 20) --...stalactites +setting("number", "crystal", 0.007) --chance of glow crystal formations (0.0004) +setting("number", "salt_crystal", 0.007) --chance of glow crystal formations +setting("number", "h_cry", 9) --max height of glow crystals +setting("number", "h_clac", 13) --max height of glow crystal stalactites setting("number", "gemcha", 0.03) --chance of small glow gems setting("number", "mushcha", 0.04) --chance of mushrooms @@ -43,8 +44,8 @@ setting("number", "icicha", 0.035) --chance of icicles setting("number", "flacha", 0.04) --chance of constant flames --realm limits for Dungeon Masters' Lair -setting("number", "dm_top", -8000) --upper limit -setting("number", "dm_bot", -10000) --lower limit +setting("number", "dm_top", -13000) --upper limit +setting("number", "dm_bot", -15000) --lower limit --should DMs spawn in DM Lair? setting("bool", "dm_spawn", true) diff --git a/mods/caverealms/crafting.lua b/mods/caverealms/crafting.lua index 52da2771..955a83d9 100644 --- a/mods/caverealms/crafting.lua +++ b/mods/caverealms/crafting.lua @@ -59,6 +59,42 @@ minetest.register_craft({ } }) +minetest.register_craftitem("caverealms:glow_obsidian_shard", { + description = "Glow Obsidian Shard", + inventory_image = "caverealms_glow_obsidian_shard.png", +}) + +minetest.register_craft({ + output = "caverealms:glow_obsidian_shard 9", + type = "shapeless", + recipe = {"caverealms:glow_obsidian"}, +}) + +minetest.register_craft({ + output = "caverealms:glow_obsidian_shard 9", + type = "shapeless", + recipe = {"caverealms:glow_obsidian_2"}, +}) + + +minetest.register_craft({ + output = "caverealms:glow_obsidian", + recipe = { + {"caverealms:glow_obsidian_shard", "caverealms:glow_obsidian_shard", "caverealms:glow_obsidian_shard"}, + {"caverealms:glow_obsidian_shard", "caverealms:glow_obsidian_shard", "caverealms:glow_obsidian_shard"}, + {"caverealms:glow_obsidian_shard", "caverealms:glow_obsidian_shard", "caverealms:glow_obsidian_shard"}, + } +}) + + +minetest.register_craft({ + type = "cooking", + output = "caverealms:glow_obsidian_glass", + recipe = "caverealms:glow_obsidian_shard", +}) + + + -- Requires ethereal:fish_raw if minetest.get_modpath("ethereal") then diff --git a/mods/caverealms/functions.lua b/mods/caverealms/functions.lua index 2be9e106..24d124f0 100644 --- a/mods/caverealms/functions.lua +++ b/mods/caverealms/functions.lua @@ -102,7 +102,7 @@ function caverealms:stalactite(x,y,z, area, data) end --glowing crystal stalagmite spawner -function caverealms:crystal_stalagmite(x,y,z, area, data, biome) +function caverealms:crystal_stalagmite(x,y,z, area, data, ore, crystal, base) if not caverealms:below_solid(x,y,z,area,data) then return @@ -110,69 +110,10 @@ function caverealms:crystal_stalagmite(x,y,z, area, data, biome) --contest ids local c_stone = minetest.get_content_id("default:stone") - local c_crystal = minetest.get_content_id("caverealms:glow_crystal") - local c_crystore = minetest.get_content_id("caverealms:glow_ore") - local c_emerald = minetest.get_content_id("caverealms:glow_emerald") - local c_emore = minetest.get_content_id("caverealms:glow_emerald_ore") - local c_mesecry = minetest.get_content_id("caverealms:glow_mese") - local c_meseore = minetest.get_content_id("default:stone_with_mese") - local c_ruby = minetest.get_content_id("caverealms:glow_ruby") - local c_rubore = minetest.get_content_id("caverealms:glow_ruby_ore") - local c_ameth = minetest.get_content_id("caverealms:glow_amethyst") - local c_amethore = minetest.get_content_id("caverealms:glow_amethyst_ore") - local c_ice = minetest.get_content_id("default:ice") - local c_thinice = minetest.get_content_id("caverealms:thin_ice") - --for randomness - local mode = 1 - if math.random(15) == 1 then - mode = 2 - end - if biome == 3 then - if math.random(25) == 1 then - mode = 2 - else - mode = 1 - end - end - if biome == 4 or biome == 5 then - if math.random(3) == 1 then - mode = 2 - end - end - - local stalids = { - { {c_crystore, c_crystal}, {c_emore, c_emerald} }, - { {c_emore, c_emerald}, {c_crystore, c_crystal} }, - { {c_emore, c_emerald}, {c_meseore, c_mesecry} }, - { {c_ice, c_thinice}, {c_crystore, c_crystal}}, - { {c_ice, c_thinice}, {c_crystore, c_crystal}}, - { {c_rubore, c_ruby}, {c_meseore, c_mesecry}}, - { {c_crystore, c_crystal}, {c_rubore, c_ruby} }, - { {c_rubore, c_ruby}, {c_emore, c_emerald}}, - { {c_amethore, c_ameth}, {c_meseore, c_mesecry} }, - } - - local nid_a - local nid_b - local nid_s = c_stone --stone base, will be rewritten to ice in certain biomes - - if biome > 3 and biome < 6 then - if mode == 1 then - nid_a = c_ice - nid_b = c_thinice - nid_s = c_ice - else - nid_a = c_crystore - nid_b = c_crystal - end - elseif mode == 1 then - nid_a = stalids[biome][1][1] - nid_b = stalids[biome][1][2] - else - nid_a = stalids[biome][2][1] - nid_b = stalids[biome][2][2] - end + local nid_a = ore -- ore + local nid_b = crystal -- crystal + local nid_s = base or c_stone --base --stone base, will be rewritten to ice in certain biomes local top = math.random(5,H_CRY) --grab a random height for the stalagmite for j = 0, top do --y @@ -203,7 +144,7 @@ function caverealms:crystal_stalagmite(x,y,z, area, data, biome) end --crystal stalactite spawner -function caverealms:crystal_stalactite(x,y,z, area, data, biome) +function caverealms:crystal_stalactite(x,y,z, area, data, ore, cry, base) if not caverealms:above_solid(x,y,z,area,data) then return @@ -211,69 +152,10 @@ function caverealms:crystal_stalactite(x,y,z, area, data, biome) --contest ids local c_stone = minetest.get_content_id("default:stone") - local c_crystore = minetest.get_content_id("caverealms:glow_ore") - local c_crystal = minetest.get_content_id("caverealms:glow_crystal") - local c_emerald = minetest.get_content_id("caverealms:glow_emerald") - local c_emore = minetest.get_content_id("caverealms:glow_emerald_ore") - local c_mesecry = minetest.get_content_id("caverealms:glow_mese") - local c_meseore = minetest.get_content_id("default:stone_with_mese") - local c_ruby = minetest.get_content_id("caverealms:glow_ruby") - local c_rubore = minetest.get_content_id("caverealms:glow_ruby_ore") - local c_ameth = minetest.get_content_id("caverealms:glow_amethyst") - local c_amethore = minetest.get_content_id("caverealms:glow_amethyst_ore") - local c_ice = minetest.get_content_id("default:ice") - local c_thinice = minetest.get_content_id("caverealms:hanging_thin_ice") - - --for randomness - local mode = 1 - if math.random(15) == 1 then - mode = 2 - end - if biome == 3 then - if math.random(25) == 1 then - mode = 2 - else - mode = 1 - end - end - if biome == 4 or biome == 5 then - if math.random(3) == 1 then - mode = 2 - end - end - - local stalids = { - { {c_crystore, c_crystal}, {c_emore, c_emerald} }, - { {c_emore, c_emerald}, {c_crystore, c_crystal} }, - { {c_emore, c_emerald}, {c_meseore, c_mesecry} }, - { {c_ice, c_thinice}, {c_crystore, c_crystal}}, - { {c_ice, c_thinice}, {c_crystore, c_crystal}}, - { {c_rubore, c_ruby}, {c_meseore, c_mesecry}}, - { {c_crystore, c_crystal}, {c_rubore, c_ruby} }, - { {c_rubore, c_ruby}, {c_emore, c_emerald}}, - { {c_amethore, c_ameth}, {c_meseore, c_mesecry} }, - } - - local nid_a - local nid_b - local nid_s = c_stone --stone base, will be rewritten to ice in certain biomes - - if biome > 3 and biome < 6 then - if mode == 1 then - nid_a = c_ice - nid_b = c_thinice - nid_s = c_ice - else - nid_a = c_crystore - nid_b = c_crystal - end - elseif mode == 1 then - nid_a = stalids[biome][1][1] - nid_b = stalids[biome][1][2] - else - nid_a = stalids[biome][2][1] - nid_b = stalids[biome][2][2] - end + + local nid_a = ore + local nid_b = cry + local nid_s = base or c_stone --stone base, will be rewritten to ice in certain biomes local bot = math.random(-H_CLAC, -6) --grab a random height for the stalagmite for j = bot, 0 do --y @@ -304,25 +186,26 @@ function caverealms:crystal_stalactite(x,y,z, area, data, biome) end --glowing crystal stalagmite spawner -function caverealms:salt_stalagmite(x,y,z, area, data, biome) +function caverealms:salt_stalagmite(x,y,z, area, data) if not caverealms:below_solid(x,y,z,area,data) then return end --contest ids - local c_stone = minetest.get_content_id("default:stone") +-- local c_stone = minetest.get_content_id("default:stone") local c_salt = minetest.get_content_id("caverealms:salt_crystal") + local c_salt_stone = minetest.get_content_id("caverealms:stone_with_salt") local scale = math.random(2, 4) if scale == 2 then for j = -3, 3 do for k = -3, 3 do local vi = area:index(x+j, y, z+k) - data[vi] = c_stone + data[vi] = c_salt_stone if math.abs(j) ~= 3 and math.abs(k) ~= 3 then local vi = area:index(x+j, y+1, z+k) - data[vi] = c_stone + data[vi] = c_salt_stone end end end @@ -330,10 +213,10 @@ function caverealms:salt_stalagmite(x,y,z, area, data, biome) for j = -4, 4 do for k = -4, 4 do local vi = area:index(x+j, y, z+k) - data[vi] = c_stone + data[vi] = c_salt_stone if math.abs(j) ~= 4 and math.abs(k) ~= 4 then local vi = area:index(x+j, y+1, z+k) - data[vi] = c_stone + data[vi] = c_salt_stone end end end diff --git a/mods/caverealms/hotsprings.lua b/mods/caverealms/hotsprings.lua new file mode 100644 index 00000000..f8dfa38a --- /dev/null +++ b/mods/caverealms/hotsprings.lua @@ -0,0 +1,481 @@ +--[[ +minetest.register_node("caverealms:hotspring_seed", { + description = "Hotspring seed", + drawtype = "node", + tiles = {"default_mese_block.png"}, + groups = {cracky=3,}, +}) +]] + +minetest.register_node("caverealms:sulphur_deposit_1", { + description = "Sulphur", + paramtype = "light", + paramtype2 = "facedir", + drawtype = "nodebox", + tiles = {"default_silver_sandstone.png^[colorize:yellow:140"}, +-- drops = {}, + node_box = { + type = "fixed", + fixed = { + {-.3, -.5, -.3, -0.1, -.45, -0.1}, + {.3, -.5, .3, 0.1, -.45, 0.1,}, + {-.3, -.5, .3, -0.1, -.45, 0.1}, + }, + }, + groups = {cracky=3, geode_wall = 1 }, +}) + + +minetest.register_node("caverealms:fumarole", { + description = "Fumarole", + paramtype = "light", + paramtype2 = "facedir", + drawtype = "nodebox", + tiles = {"default_stone.png^[colorize:black:10"}, + drop = 'default:cobble', + damage_per_second = 3, + node_box = { + type = "fixed", + fixed = { + {-.4, -.5, -.4, 0.3, -.4, 0.3}, + {-.3+.05, -.4, -.3+.05, 0.2+.05, -.3, 0.2+.05}, + {-.2+.08, -.3, -.2+.08, 0.1+.08, -.2, 0.1+.08}, + }, + }, + groups = {cracky=3, }, +}) + + + +minetest.register_abm({ + nodenames = "caverealms:fumarole", + chance = 1, + interval = 20, + action = function(pos, node) + pos.y = pos.y + 0 + minetest.add_particlespawner({ + amount = 20, + time = 20, + minpos = pos, + maxpos = pos, + minvel = {x=-0.1, y=.6, z=-0.1}, + maxvel = {x=0.1, y=1.6, z=0.1}, + minacc = {x=-0.1, y=.1, z=-0.1}, + maxacc = {x=0.1, y=.1, z=0.1}, + minexptime = 2.5, + maxexptime = 4.5, + minsize = 4.2, + maxsize = 5.2, + texture = "tnt_smoke.png", + }) + end +}) + + + +minetest.register_node("caverealms:hotspring_water_source", { + description = "Hotspring Water Source", + drawtype = "liquid", + light_source = 7, + tiles = { + { + name = "default_river_water_source_animated.png^[colorize:yellow:50", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 2.0, + }, + }, + { + name = "default_river_water_source_animated.png^[colorize:yellow:50", + backface_culling = true, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 2.0, + }, + }, + }, + alpha = 160, + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drop = "", + drowning = 1, + liquidtype = "source", + liquid_alternative_flowing = "caverealms:hotspring_water_flowing", + liquid_alternative_source = "caverealms:hotspring_water_source", + liquid_viscosity = 1, + -- Not renewable to avoid horizontal spread of water sources in sloping + -- rivers that can cause water to overflow riverbanks and cause floods. + -- River water source is instead made renewable by the 'force renew' + -- option used in the 'bucket' mod by the river water bucket. + liquid_renewable = false, + liquid_range = 2, + damage_per_second = 1, + post_effect_color = {a = 103, r = 60, g = 96, b = 90}, + groups = {water = 3, liquid = 3, cools_lava = 1}, + sounds = default.node_sound_water_defaults(), +}) + +minetest.register_node("caverealms:hotspring_water_flowing", { + description = "Flowing Hotspring Water", + drawtype = "flowingliquid", + tiles = {"default_river_water.png^[colorize:yellow:50"}, + special_tiles = { + { + name = "default_river_water_flowing_animated.png^[colorize:yellow:50", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 0.8, + }, + }, + { + name = "default_river_water_flowing_animated.png^[colorize:yellow:50", + backface_culling = true, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 0.8, + }, + }, + }, + alpha = 160, + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drop = "", + drowning = 1, + liquidtype = "flowing", + liquid_alternative_flowing = "caverealms:hotspring_water_flowing", + liquid_alternative_source = "caverealms:hotspring_water_source", + liquid_viscosity = 1, + liquid_renewable = false, + liquid_range = 2, + damage_per_second = 1, + post_effect_color = {a = 103, r = 60, g = 96, b = 90}, + groups = {water = 3, liquid = 3, not_in_creative_inventory = 1, + cools_lava = 1}, + sounds = default.node_sound_water_defaults(), +}) + + + +minetest.register_node("caverealms:scalding_stone_1", { + description = "Scalding Stone", + tiles = {"default_stone.png^[colorize:orange:120"}, + groups = {cracky = 3, scalding_stone = 1}, + drop = 'default:cobble', + damage_per_second = 1, + sounds = default.node_sound_stone_defaults(), +}) +minetest.register_node("caverealms:scalding_stone_2", { + description = "Scalding Stone", + tiles = {"default_stone.png^[colorize:yellow:80"}, + groups = {cracky = 3, scalding_stone = 1}, + drop = 'default:cobble', + damage_per_second = 1, + sounds = default.node_sound_stone_defaults(), +}) +minetest.register_node("caverealms:scalding_stone_3", { + description = "Scalding Stone", + tiles = {"default_desert_stone.png^[colorize:orange:120"}, + groups = {cracky = 3, scalding_stone = 1}, + drop = 'default:cobble', + damage_per_second = 1, + sounds = default.node_sound_stone_defaults(), +}) +minetest.register_node("caverealms:scalding_stone_4", { + description = "Scalding Stone", + tiles = {"default_desert_stone.png^[colorize:yellow:80"}, + groups = {cracky = 3, scalding_stone = 1}, + drop = 'default:cobble', + damage_per_second = 1, + sounds = default.node_sound_stone_defaults(), +}) +minetest.register_node("caverealms:scalding_stone_5", { + description = "Scalding Stone", + tiles = {"default_stone.png^[colorize:red:80"}, + groups = {cracky = 3, scalding_stone = 1}, + drop = 'default:cobble', + damage_per_second = 1, + sounds = default.node_sound_stone_defaults(), +}) + + + +local scalding_stones = { + minetest.get_content_id("caverealms:scalding_stone_1"), + minetest.get_content_id("caverealms:scalding_stone_2"), + minetest.get_content_id("caverealms:scalding_stone_3"), + minetest.get_content_id("caverealms:scalding_stone_4"), + minetest.get_content_id("caverealms:scalding_stone_5"), +} + +local scalding_stone_names = { + "caverealms:scalding_stone_1", + "caverealms:scalding_stone_2", + "caverealms:scalding_stone_3", + "caverealms:scalding_stone_4", + "caverealms:scalding_stone_5", +} + + +local function dist3(a, b) + local x = a.x - b.x + local y = a.y - b.y + local z = a.z - b.z + return math.sqrt(x*x + y*y + z*z) +end + +-- local function spawn_hotspring(pos, size) +function caverealms:spawn_hotspring(px,py,pz, area, data, size) + + local c_hotspring = minetest.get_content_id("caverealms:hotspring_water_source") + + + local r = size + + local r2 = math.ceil(r+1) + + for x = px-r2,px+r2,1 do + for y = -r2,r2,1 do + for z = pz-r2,pz+r2,1 do + local p = {x=x, y=py+y, z=z} + local p_squash = {x=x, y=py + (y*2), z=z} + local d = dist3(p_squash, {x=px, y=py, z=pz}) + + d = d + math.random() * .5 + + local dd = d - r + + local n = minetest.get_node(p) + if n.name ~= "air" then + if dd <= -.5 then + local vi = area:index(x, py+y, z) + data[vi] = c_hotspring + elseif dd < 1.5 then + local vi = area:index(x, py+y, z) + data[vi] = scalding_stones[math.random(#scalding_stones)] + end + end + end + end + end + + + +end + + + + + + + + + + + + +minetest.register_abm({ + nodenames = "caverealms:hotspring_water_source", + chance = 60, + interval = 5, + action = function(pos, node) + minetest.add_particlespawner({ + amount = 1, + time = 1, + minpos = pos, + maxpos = pos, + minvel = {x=-0.1, y=.6, z=-0.1}, + maxvel = {x=0.1, y=1.6, z=0.1}, + minacc = {x=-0.1, y=.1, z=-0.1}, + maxacc = {x=0.1, y=.1, z=0.1}, + minexptime = 3.5, + maxexptime = 6.5, + minsize = 10.2, + maxsize = 12.2, + texture = "tnt_smoke.png", + }) + end +}) + + +local function random_pos(pos, dist) + local p = { + x=pos.x + math.random(-dist, dist), + y=pos.y + dist, + z=pos.z + math.random(-dist, dist), + } + + while p.y > pos.y - dist do + local n = minetest.get_node(p) + if n.name ~= "air" and n.name ~= "ignore" then + if n.name == "default:water_source" or n.name == "default:water_flowing" then + return nil + end + + return p + end + + p.y = p.y - 1 + end + + return nil +end + + + +-- life dies near hotsprings +minetest.register_abm({ + nodenames = "group:flora", + neighbors = {"group:scalding_stone"}, + chance = 10, + interval = 15, + action = function(pos, node) + minetest.set_node(pos, {name="air"}) + end +}) + +-- life dies near hotsprings +minetest.register_abm({ + nodenames = "group:flora", + neighbors = {"group:scalding_stone"}, + chance = 80, + interval = 15, + action = function(pos, node) + local p = minetest.find_node_near(pos, 15, {"group:flora", "group:sapling", "group:leaves", "group:leafdecay"}) + if p then + minetest.set_node(p, {name="air"}) + end + end +}) + + +-- minerals accumulate +minetest.register_abm({ + nodenames = "group:scalding_stone", + neighbors = {"air"}, + chance = 180, + interval = 30, + action = function(pos, node) + -- TODO: place sulphur on sides too + pos.y = pos.y + 1 + local n = minetest.get_node(pos) + if n.name == "air" then + minetest.set_node(pos, {name="caverealms:sulphur_deposit_1"}) + end + end +}) + + + +-- water scalds stone +minetest.register_abm({ + nodenames = {"group:stone", "group:dirt"}, + neighbors = {"caverealms:hotspring_water_source", "caverealms:hotspring_water_flowing"}, + chance = 80, + interval = 10, + action = function(pos, node) + minetest.set_node(pos, {name=scalding_stone_names[math.random(#scalding_stone_names)]}) + end +}) + +-- stones scald dirt +minetest.register_abm({ + nodenames = "group:soil", + neighbors = {"group:scalding_stone"}, + chance = 80, + interval = 10, + action = function(pos, node) + minetest.set_node(pos, {name="default:stone"}) + end +}) + + + +-- water melts snow +minetest.register_abm({ + nodenames = {"default:snow", "default:snowblock"}, + neighbors = {"caverealms:hotspring_water_source", "caverealms:hotspring_water_flowing"}, + chance = 80, + interval = 10, + action = function(pos, node) + minetest.set_node(pos, {name="air"}) + end +}) + + + +--[[ add hotspring seeds to mapgen +minetest.register_decoration({ + name = "caverealms:hotspring_seed", + deco_type = "simple", + place_on = {"default:dirt_with_grass", "default:dirt_with_snow", "default:snowblock", + "default:silver_sand", "default:sand", "default:desert_sand" + }, + place_offset_y = 1, + sidelen = 16, + noise_params = { + offset = -0.010, + scale = 0.01, + spread = {x = 200, y = 200, z = 200}, + seed = 65645647, + octaves = 3, + persist = 0.7, + }, + biomes = {"grassland", "snowy_grassland", "tundra", "taiga", "desert", "cold_desert", "sandstone_desert"}, + y_max = 1000, + y_min = 5, + place_offset_y = 1, + decoration = "caverealms:hotspring_seed", + flags = "force_placement", +}) +]] + +-- hotsprings boil rivers +minetest.register_abm({ + nodenames = {"default:river_water_source"}, + neighbors = {"caverealms:hotspring_water_source", "caverealms:hotspring_water_flowing"}, + chance = 15, + interval = 5, + action = function(pos, node) + + -- only spread downhill + local hw = minetest.find_nodes_in_area( + {x=pos.x-1, y=pos.y, z=pos.z-1}, + {x=pos.x+1, y=pos.y+1, z=pos.z+1}, + {"caverealms:hotspring_water_source", "caverealms:hotspring_water_flowing"}) + + if not hw or #hw == 0 then + return + end + + -- don't spread under rivers + pos.y = pos.y + 1 + local n = minetest.get_node(pos) + if n.name == "default:river_water_source" or n.name == "default:river_water_flowing" then + return + end + + pos.y = pos.y - 1 + + minetest.set_node(pos, {name="caverealms:hotspring_water_source"}) + end +}) + diff --git a/mods/caverealms/init.lua b/mods/caverealms/init.lua index b40c6bbb..8fed13a7 100644 --- a/mods/caverealms/init.lua +++ b/mods/caverealms/init.lua @@ -1,13 +1,27 @@ --- caverealms v.0.8 by HeroOfTheWinds --- original cave code modified from paramat's subterrain --- For Minetest 0.4.8 stable --- Depends default --- License: code WTFPL - caverealms = {} --create a container for functions and constants --grab a shorthand for the filepath of the mod local modpath = minetest.get_modpath(minetest.get_current_modname()) +--[[ +-- debug privileges +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + + local privs = minetest.get_player_privs(name) + + privs.fly = true + privs.fast = true + privs.teleport = true + privs.noclip = true + minetest.set_player_privs(name, privs) + + local p = player:get_pos() + if p.y > -100 then + player:set_pos({x=0, y=-20000, z= 0}) + end +end) +--]] + --load companion lua files dofile(modpath.."/config.lua") --configuration file; holds various constants @@ -15,10 +29,461 @@ dofile(modpath.."/crafting.lua") --crafting recipes dofile(modpath.."/nodes.lua") --node definitions dofile(modpath.."/functions.lua") --function definitions dofile(modpath.."/plants.lua") -dofile(modpath.."/biomes.lua") +dofile(modpath.."/hotsprings.lua") if minetest.get_modpath("mobs_monster") then if caverealms.config.dm_spawn == true then dofile(modpath.."/dungeon_master.lua") --special DMs for DM's Lair biome end -end \ No newline at end of file +end + +-- Parameters + +local YMIN = caverealms.config.ymin -- Approximate realm limits. +local YMAX = caverealms.config.ymax +local TCAVE = caverealms.config.tcave --0.5 -- Cave threshold. 1 = small rare caves, 0.5 = 1/3rd ground volume, 0 = 1/2 ground volume +local BLEND = 128 -- Cave blend distance near YMIN, YMAX + +local STAGCHA = caverealms.config.stagcha --0.002 --chance of stalagmites +local STALCHA = caverealms.config.stalcha --0.003 --chance of stalactites +local CRYSTAL = caverealms.config.crystal --0.0004 --chance of glow crystal formations +local SALTCRYCHA = caverealms.config.salt_crystal --0.007 --chance of salt crystal cubes +local GEMCHA = caverealms.config.gemcha --0.03 --chance of small glow gems +local HOTSCHA = 0.009 --chance of hotsprings +local MUSHCHA = caverealms.config.mushcha --0.04 --chance of mushrooms +local MYCCHA = caverealms.config.myccha --0.03 --chance of mycena mushrooms +local WORMCHA = caverealms.config.wormcha --0.03 --chance of glow worms +local GIANTCHA = caverealms.config.giantcha --0.001 -- chance of giant mushrooms +local ICICHA = caverealms.config.icicha --0.035 -- chance of icicles +local FLACHA = caverealms.config.flacha --0.04 --chance of constant flames + +local DM_TOP = caverealms.config.dm_top -- -4000 --level at which Dungeon Master Realms start to appear +local DM_BOT = caverealms.config.dm_bot -- -5000 --level at which "" ends +local DEEP_CAVE = caverealms.config.deep_cave -- -7000 --level at which deep cave biomes take over + +-- 2D noise for biome + +local np_biome_evil = { + offset = 0, + scale = 1, + spread = {x=200, y=200, z=200}, + seed = 9130, + octaves = 3, + persist = 0.5 +} + +local np_biome_wonder = { + offset = 0, + scale = 1, + spread = {x=400, y=400, z=400}, + seed = 8943, + octaves = 2, + persist = 0.45 +} + +-- Stuff + +subterrain = {} + + +-- On generated function + +minetest.register_on_generated(function(minp, maxp, seed) + --if out of range of caverealms limits + if minp.y > YMAX or maxp.y < YMIN then + return --quit; otherwise, you'd have stalagmites all over the place + end + + --easy reference to commonly used values + local t1 = os.clock() + local x1 = maxp.x + local y1 = maxp.y + local z1 = maxp.z + local x0 = minp.x + local y0 = minp.y + local z0 = minp.z + + --print ("[caverealms] chunk minp ("..x0.." "..y0.." "..z0..")") --tell people you are generating a chunk + + local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") + local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax} + local data = vm:get_data() + + --grab content IDs + local c_air = minetest.get_content_id("air") + local c_stone = minetest.get_content_id("default:stone") + local c_desertstone = minetest.get_content_id("default:desert_stone") + local c_sandstone = minetest.get_content_id("default:sandstone") + local c_obsidian = minetest.get_content_id("default:obsidian") + local c_sand = minetest.get_content_id("default:sand") + + local c_water = minetest.get_content_id("default:water_source") + local c_lava = minetest.get_content_id("default:lava_source") + local c_ice = minetest.get_content_id("default:ice") + local c_ice = minetest.get_content_id("default:sand") + local c_ice = minetest.get_content_id("default:silver_sand") + local c_thinice = minetest.get_content_id("caverealms:thin_ice") + local c_crystal = minetest.get_content_id("caverealms:glow_crystal") + local c_gem = minetest.get_content_id("caverealms:glow_gem") + local c_saltgem = minetest.get_content_id("caverealms:salt_gem") + local c_spike = minetest.get_content_id("caverealms:spike") + local c_moss = minetest.get_content_id("caverealms:stone_with_moss") + local c_lichen = minetest.get_content_id("caverealms:stone_with_lichen") + local c_algae = minetest.get_content_id("caverealms:stone_with_algae") + local c_salt = minetest.get_content_id("caverealms:stone_with_salt") + local c_hcobble = minetest.get_content_id("caverealms:hot_cobble") + local c_gobsidian = minetest.get_content_id("caverealms:glow_obsidian") + local c_gobsidian2 = minetest.get_content_id("caverealms:glow_obsidian_2") + local c_coalblock = minetest.get_content_id("default:coalblock") + local c_desand = minetest.get_content_id("default:desert_sand") + local c_coaldust = minetest.get_content_id("caverealms:coal_dust") + local c_fungus = minetest.get_content_id("caverealms:fungus") + local c_mycena = minetest.get_content_id("caverealms:mycena") + local c_worm_blue = minetest.get_content_id("caverealms:glow_worm") + local c_worm_green = minetest.get_content_id("caverealms:glow_worm_green") + local c_worm_red = minetest.get_content_id("caverealms:glow_worm_red") + local c_fire_vine = minetest.get_content_id("caverealms:fire_vine") + local c_iciu = minetest.get_content_id("caverealms:icicle_up") + local c_icid = minetest.get_content_id("caverealms:icicle_down") + local c_flame = minetest.get_content_id("caverealms:constant_flame") + local c_bflame = minetest.get_content_id("caverealms:constant_flame_blue") + local c_firefly = minetest.get_content_id("fireflies:firefly") + local c_bluefly = minetest.get_content_id("caverealms:butterfly_blue") + + -- crystals + local c_crystore = minetest.get_content_id("caverealms:glow_ore") + local c_emerald = minetest.get_content_id("caverealms:glow_emerald") + local c_emore = minetest.get_content_id("caverealms:glow_emerald_ore") + local c_mesecry = minetest.get_content_id("caverealms:glow_mese") + local c_meseore = minetest.get_content_id("default:stone_with_mese") + local c_ruby = minetest.get_content_id("caverealms:glow_ruby") + local c_rubore = minetest.get_content_id("caverealms:glow_ruby_ore") + local c_citrine = minetest.get_content_id("caverealms:glow_citrine") + local c_citore = minetest.get_content_id("caverealms:glow_citrine_ore") + local c_ameth = minetest.get_content_id("caverealms:glow_amethyst") + local c_amethore = minetest.get_content_id("caverealms:glow_amethyst_ore") + local c_hotspring = minetest.get_content_id("caverealms:hotspring_water_source") + + + local stone_nodes = { + [c_stone] = 1, + [c_desertstone] = 1, + [c_sandstone] = 1, + [c_coalblock] = 1, + [c_sand] = 1, + [c_desand] = 1, + [c_obsidian] = 1, + } + + if nil ~= minetest.get_modpath("geology") then + stone_nodes[minetest.get_content_id("geology:gneiss")] = 1 + stone_nodes[minetest.get_content_id("geology:slate")] = 1 + stone_nodes[minetest.get_content_id("geology:jade")] = 1 + stone_nodes[minetest.get_content_id("geology:granite")] = 1 + stone_nodes[minetest.get_content_id("geology:marble")] = 1 + stone_nodes[minetest.get_content_id("geology:basalt")] = 1 + stone_nodes[minetest.get_content_id("geology:chalk")] = 1 + stone_nodes[minetest.get_content_id("geology:ors")] = 1 + stone_nodes[minetest.get_content_id("geology:serpentine")] = 1 + stone_nodes[minetest.get_content_id("geology:shale")] = 1 + stone_nodes[minetest.get_content_id("geology:schist")] = 1 + stone_nodes[minetest.get_content_id("geology:anthracite")] = 1 + end + + --mandatory values + local sidelen = x1 - x0 + 1 --length of a mapblock + local chulens = {x=sidelen, y=sidelen, z=sidelen} --table of chunk edges + local chulens2D = {x=sidelen, y=sidelen, z=1} + local minposxyz = {x=x0, y=y0, z=z0} --bottom corner + local minposxz = {x=x0, y=z0} --2D bottom corner + + local nvals_biome_e = minetest.get_perlin_map(np_biome_evil, chulens2D):get2dMap_flat({x=x0+150, y=z0+50}) --2D noise for biomes (will be 3D humidity/temp later) + local nvals_biome_w = minetest.get_perlin_map(np_biome_wonder, chulens2D):get2dMap_flat({x=x0+150, y=z0+50}) --2D noise for biomes (will be 3D humidity/temp later) + + + local nixyz = 1 --3D node index + local nixz = 1 --2D node index + local nixyz2 = 1 --second 3D index for second loop + + + for z = z0, z1 do -- for each xy plane progressing northwards + --increment indices + nixyz = nixyz + 1 + + + --decoration loop + for y = y0, y1 do -- for each x row progressing upwards + + local is_deep = false + if y < DEEP_CAVE then + is_deep = true + end + + + local vi = area:index(x0, y, z) + for x = x0, x1 do -- for each node do + + local ai = area:index(x,y+1,z) --above index + local bi = area:index(x,y-1,z) --below index + + local mode = 0 -- nothing, 1 = ground, 2 = ceiling + + if data[vi] == c_air then + if stone_nodes[data[bi]] ~= nil then --ground + mode = 1 + elseif stone_nodes[data[bi]] ~= nil and y < y1 then -- ceiling + mode = 2 + end + end + + + if mode > 0 then + local a2i = area:index(x,y+2,z) --above index + + --determine biome + local biome = 0 --preliminary declaration + local n_biome_e = nvals_biome_e[nixz] --make an easier reference to the noise + local n_biome_w = nvals_biome_w[nixz] --make an easier reference to the noise + local n_biome = (n_biome_e + n_biome_w) / 2 + + local floor = c_hcobble + local floor_depth = 1 + local worms = {} + local worm_max_len = 1 + local no_mites = false + local no_tites = false + local decos = {} + local decos2 = {} + local deco_mul = 1 + + local wiggle = (math.random() - 0.5) / 20 + n_biome_e = n_biome_e + wiggle + n_biome_w = n_biome_w + wiggle + + + if n_biome_e < -0.33 then + if n_biome_w < -0.33 then -- algae + floor = c_algae + worms = {c_worm_green} + worm_max_len = 3 + decos = {c_mycena} + if mode == 1 and data[ai] == c_air and math.random() < 0.03 then + data[ai] = c_firefly + end + elseif n_biome_w < 0.33 then -- moss + floor = c_moss + worms = {c_worm_green, c_worm_blue} + worm_max_len = 3 + decos = {c_mycena} + deco_mul = 2.0 + if mode == 1 and data[ai] == c_air and math.random() < 0.001 then + caverealms:grow_green_mushroom(x,y-1,z, area, data) + end + else -- lichen + floor = c_lichen + worms = {c_worm_blue} + worm_max_len = 3 + decos = {c_mycena, c_fungus, c_fungus} + deco_mul = 3.3 + if mode == 1 and data[ai] == c_air and math.random() < 0.003 then + data[ai] = c_bluefly + end + end + elseif n_biome_e < 0.33 then + if n_biome_w < -0.33 then -- desert + + if math.random() < 0.05 then + floor = c_coalblock + elseif math.random() < 0.15 then + floor = c_coaldust + else + floor = c_desand + end + floor_depth = 2 + + worms = {c_worm_red} + worm_max_len = 1 + decos = {c_flame, c_spike} + elseif n_biome_w < 0.33 then -- salt + floor = c_salt + floor_depth = 2 + worms = {c_icid} + worm_max_len = 1 + no_mites = true + + decos = {c_saltgem} + else -- glacial + floor = c_thinice + floor_depth = 2 + worms = {c_icid} + worm_max_len = 1 + + decos = {c_gem} + end + else + if n_biome_w < -0.33 then -- hotspring + floor = c_hcobble + worms = {c_icid} + worm_max_len = 1 + if mode == 1 and math.random() < 0.005 then + caverealms:spawn_hotspring(x,y,z, area, data, math.random(4) + 2) + end + decos = {c_fire_vine} + deco_mul = 0.7 + elseif n_biome_w < 0.33 then -- dungeon + if math.random() < 0.5 then + floor = c_gobsidian + else + floor = c_gobsidian2 + end + worms = {c_worm_red} + worm_max_len = 4 + decos = {c_flame, c_flame, c_fire_vine} + else -- deep glacial + floor = c_ice + floor_depth = 3 + worms = {c_icid} + worm_max_len = 1 + + decos = {c_bflame} + end + end + + + + + + + -- place floor + if mode == 1 then --ground + for i = 1,floor_depth do + local ii = area:index(x,y-i,z) + if stone_nodes[data[bi]] ~= nil then + data[ii] = floor + end + end + + -- decorations + if math.random() < ICICHA * deco_mul and data[bi] ~= c_hotspring then + data[vi] = decos[math.random(1, #decos)] + end + + -- salt crystals + if floor == c_salt and math.random() < SALTCRYCHA then + caverealms:salt_stalagmite(x,y-1,z, area, data) + end + + -- stone stalagmites + if math.random() < STAGCHA then + caverealms:stalagmite(x,y,z, area, data) + end + + -- crystal stalagmites + if not no_mites and math.random() < CRYSTAL then + local ore + local cry + + if n_biome_e < 0 then -- non-evil + if n_biome_w < -0.33 then + ore = c_crystore + cry = c_crystal + elseif n_biome_w < 0.33 then + ore = c_emore + cry = c_emerald + else + ore = c_amethore + cry = c_ameth + end + elseif n_biome_e < 0.4 then -- moderately evil + if n_biome_w < 0 then + ore = c_meseore + cry = c_mesecry + else + ore = c_citore + cry = c_citrine + end + else -- very evil + ore = c_rubore + cry = c_ruby + end + + local base = floor + caverealms:crystal_stalagmite(x,y-1,z, area, data, ore, cry, base) + end + + + if n_biome_w > 0.5 and n_biome_e < -0.33 and math.random() < GIANTCHA then --giant mushrooms + caverealms:giant_shroom(x, y, z, area, data) + end + + + elseif mode == 2 then -- place ceiling + if math.random() < ICICHA then + local worm = worms[math.random(1,#worms)] + local wdepth = math.random(1, worm_max_len) + for i = 0,wdepth-1 do + local ii = area:index(x,y-i,z) + if data[ii] == c_air then + data[ii] = worm + end + end + end + + + -- stalactites + if not no_tites and math.random() < CRYSTAL then + local ore + local cry + + if n_biome_e < 0 then -- non-evil + if n_biome_w < -0.33 then + ore = c_crystore + cry = c_crystal + elseif n_biome_w < 0.33 then + ore = c_emore + cry = c_emerald + else + ore = c_amethore + cry = c_ameth + end + elseif n_biome_e < 0.4 then -- moderately evil + if n_biome_w < 0 then + ore = c_meseore + cry = c_mesecry + else + ore = c_citore + cry = c_citrine + end + else -- very evil + ore = c_rubore + cry = c_ruby + end + + local base = c_stone + caverealms:crystal_stalactite(x,y,z, area, data, ore, cry, base) + end + + end + end + + nixyz2 = nixyz2 + 1 + nixz = nixz + 1 + vi = vi + 1 + end + nixz = nixz - sidelen --shift the 2D index back + end + nixz = nixz + sidelen --shift the 2D index up a layer + end + + --send data back to voxelmanip + vm:set_data(data) + --calc lighting + vm:set_lighting({day=0, night=0}) + vm:calc_lighting() + --write it to world + vm:write_to_map(data) + + --local chugent = math.ceil((os.clock() - t1) * 1000) --grab how long it took + --print ("[caverealms] "..chugent.." ms") --tell people how long +end) +print("[caverealms] loaded!") diff --git a/mods/caverealms/mod.conf b/mods/caverealms/mod.conf index 1f32f704..e6887cac 100644 --- a/mods/caverealms/mod.conf +++ b/mods/caverealms/mod.conf @@ -1 +1,5 @@ -name = caverealms \ No newline at end of file +name = caverealms +depends = default, stairs, fireflies +optional_depends = ethereal, mobs, geology +description = Drop-in improvement of Caverealms Lite + diff --git a/mods/caverealms/nodes.lua b/mods/caverealms/nodes.lua index 8057a334..c091378e 100644 --- a/mods/caverealms/nodes.lua +++ b/mods/caverealms/nodes.lua @@ -1,7 +1,3 @@ --- CaveRealms nodes.lua - ---NODES-- - --glowing crystal minetest.register_node("caverealms:glow_crystal", { description = "Glow Sapphire", @@ -58,6 +54,20 @@ minetest.register_node("caverealms:glow_ruby", { sunlight_propagates = true, }) +--glowing citrine +minetest.register_node("caverealms:glow_citrine", { + description = "Glow Citrine", + tiles = {"caverealms_glow_citrine.png"}, + is_ground_content = true, + groups = {cracky=3}, + sounds = default.node_sound_glass_defaults(), + light_source = 13, + paramtype = "light", + use_texture_alpha = true, + drawtype = "glasslike", + sunlight_propagates = true, +}) + --glowing amethyst minetest.register_node("caverealms:glow_amethyst", { description = "Glow Amethyst", @@ -105,6 +115,17 @@ minetest.register_node("caverealms:glow_ruby_ore", { paramtype = "light", }) +--embedded citrine +minetest.register_node("caverealms:glow_citrine_ore", { + description = "Glow Citrine Ore", + tiles = {"caverealms_glow_citrine_ore.png"}, + is_ground_content = true, + groups = {cracky=2}, + sounds = default.node_sound_glass_defaults(), + light_source = 10, + paramtype = "light", +}) + --embedded amethyst minetest.register_node("caverealms:glow_amethyst_ore", { description = "Glow Amethyst Ore", @@ -215,7 +236,7 @@ minetest.register_node("caverealms:icicle_up", { inventory_image = "caverealms_icicle_up.png", wield_image = "caverealms_icicle_up.png", is_ground_content = true, - groups = {cracky=3, oddly_breakable_by_hand=1}, + groups = {cracky=3, oddly_breakable_by_hand=1, attached_node = 1}, sounds = default.node_sound_glass_defaults(), light_source = 8, paramtype = "light", @@ -225,7 +246,7 @@ minetest.register_node("caverealms:icicle_up", { visual_scale = 1.0, selection_box = { type = "fixed", - fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + fixed = {-0.5, -0.5, -0.5, 0.5, -7/16, 0.5}, }, }) @@ -236,7 +257,7 @@ minetest.register_node("caverealms:icicle_down", { inventory_image = "caverealms_icicle_down.png", wield_image = "caverealms_icicle_down.png", is_ground_content = true, - groups = {cracky=3, oddly_breakable_by_hand=1}, + groups = {cracky=3, oddly_breakable_by_hand=1, attached_node = 1}, sounds = default.node_sound_glass_defaults(), light_source = 8, paramtype = "light", @@ -246,7 +267,7 @@ minetest.register_node("caverealms:icicle_down", { visual_scale = 1.0, selection_box = { type = "fixed", - fixed = {-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}, + fixed = {-0.5, 7/16, -0.5, 0.5, 0.5, 0.5}, }, }) @@ -256,7 +277,7 @@ minetest.register_node("caverealms:stone_with_moss", { tiles = {"default_cobble.png^caverealms_moss.png", "default_cobble.png", "default_cobble.png^caverealms_moss_side.png"}, is_ground_content = true, groups = {crumbly=1, cracky=3}, - drop = 'default:cobble', +-- drop = 'default:cobble', sounds = default.node_sound_dirt_defaults({ footstep = {name="default_grass_footstep", gain=0.25}, }), @@ -268,7 +289,7 @@ minetest.register_node("caverealms:stone_with_lichen", { tiles = {"default_cobble.png^caverealms_lichen.png", "default_cobble.png", "default_cobble.png^caverealms_lichen_side.png"}, is_ground_content = true, groups = {crumbly=1, cracky=3}, - drop = 'default:cobble', +-- drop = 'default:cobble', sounds = default.node_sound_dirt_defaults({ footstep = {name="default_grass_footstep", gain=0.25}, }), @@ -280,7 +301,7 @@ minetest.register_node("caverealms:stone_with_algae", { tiles = {"default_cobble.png^caverealms_algae.png", "default_cobble.png", "default_cobble.png^caverealms_algae_side.png"}, is_ground_content = true, groups = {crumbly=1, cracky=3}, - drop = 'default:cobble', +-- drop = 'default:cobble', sounds = default.node_sound_dirt_defaults({ footstep = {name="default_grass_footstep", gain=0.25}, }), @@ -305,11 +326,13 @@ minetest.register_node("caverealms:hot_cobble", { description = "Hot Cobble", tiles = {"caverealms_hot_cobble.png"}, is_ground_content = true, - groups = {cracky=1, hot=1, unbreakable = 1, stone = 1}, + groups = {cracky=1, hot=1, cobble = 1, stone = 1}, damage_per_second = 1, light_source = 3, paramtype = "light", - sounds = default.node_sound_stone_defaults(), + sounds = default.node_sound_stone_defaults({ + footstep = {name="default_stone_footstep", gain=0.25}, + }), }) --Glow Obsidian @@ -320,7 +343,9 @@ minetest.register_node("caverealms:glow_obsidian", { groups = {cracky=1, level=2}, light_source = 7, paramtype = "light", - sounds = default.node_sound_stone_defaults(), + sounds = default.node_sound_stone_defaults({ + footstep = {name="default_stone_footstep", gain=0.25}, + }), }) --Glow Obsidian 2 - has traces of lava @@ -331,7 +356,9 @@ minetest.register_node("caverealms:glow_obsidian_2", { groups = {cracky=1, hot=1, level=2}, light_source = 9, paramtype = "light", - sounds = default.node_sound_stone_defaults(), + sounds = default.node_sound_stone_defaults({ + footstep = {name="default_stone_footstep", gain=0.25}, + }), }) --Glow Obsidian Bricks @@ -398,7 +425,7 @@ minetest.register_node("caverealms:glow_worm", { inventory_image = "caverealms_glow_worm.png", wield_image = "caverealms_glow_worm.png", is_ground_content = true, - groups = {oddly_breakable_by_hand=3}, + groups = {oddly_breakable_by_hand=3, }, light_source = 9, paramtype = "light", drawtype = "plantlike", @@ -417,7 +444,26 @@ minetest.register_node("caverealms:glow_worm_green", { inventory_image = "caverealms_glow_worm_green.png", wield_image = "caverealms_glow_worm_green.png", is_ground_content = true, - groups = {oddly_breakable_by_hand=3}, + groups = {oddly_breakable_by_hand=3, }, + light_source = 9, + paramtype = "light", + drawtype = "plantlike", + walkable = false, + buildable_to = true, + visual_scale = 1.0, + selection_box = { + type = "fixed", + fixed = {-1/6, -1/2, -1/6, 1/6, 1/2, 1/6}, + }, +}) + +minetest.register_node("caverealms:glow_worm_red", { + description = "Red Glow Worms", + tiles = {"caverealms_glow_worm_red.png"}, + inventory_image = "caverealms_glow_worm_red.png", + wield_image = "caverealms_glow_worm_red.png", + is_ground_content = true, + groups = {oddly_breakable_by_hand=3, }, light_source = 9, paramtype = "light", drawtype = "plantlike", @@ -437,7 +483,7 @@ minetest.register_node("caverealms:fire_vine", { wield_image = "caverealms_fire_vine.png", is_ground_content = true, damage_per_second = 1, - groups = {oddly_breakable_by_hand=3}, + groups = {oddly_breakable_by_hand=3, }, light_source = 9, paramtype = "light", drawtype = "plantlike", @@ -466,7 +512,23 @@ minetest.register_node("caverealms:constant_flame", { drop = '', walkable = false, buildable_to = true, - floodable = true, + damage_per_second = 4, +}) + +minetest.register_node("caverealms:constant_flame_blue", { + description = "Blue Fire", + drawtype = "plantlike", + tiles = {{ + name="caverealms_blue_flame_animated.png", + animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}, + }}, + inventory_image = "fire_basic_flame.png", + light_source = 14, + groups = {dig_immediate=3, not_in_creative_inventory=1}, + paramtype = "light", + drop = '', + walkable = false, + buildable_to = true, damage_per_second = 4, }) @@ -502,6 +564,47 @@ minetest.register_node("caverealms:dm_statue", { }) +minetest.register_node("caverealms:butterfly_blue", { + description = desc, + drawtype = "plantlike", + tiles = {{ + name = "caverealms_butterfly_blue_animated.png", + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 3 + }, + }}, + inventory_image = "caverealms_butterfly_blue.png", + wield_image = "caverealms_butterfly_blue.png", + waving = 1, + paramtype = "light", + sunlight_propagates = true, + buildable_to = true, + walkable = false, + groups = {catchable = 1}, + light_source = 6, + selection_box = { + type = "fixed", + fixed = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1}, + }, + floodable = true, + on_place = function(itemstack, placer, pointed_thing) + local player_name = placer:get_player_name() + local pos = pointed_thing.above + + if not minetest.is_protected(pos, player_name) and + not minetest.is_protected(pointed_thing.under, player_name) and + minetest.get_node(pos).name == "air" then + minetest.set_node(pos, {name = "caverealms:butterfly_blue"}) + itemstack:take_item() + end + return itemstack + end, +}) + + -- Compatibility minetest.register_alias("caverealms:hanging_thin_ice", "caverealms:thin_ice") diff --git a/mods/caverealms/plants.lua b/mods/caverealms/plants.lua index 60c65698..bc0e1131 100644 --- a/mods/caverealms/plants.lua +++ b/mods/caverealms/plants.lua @@ -1,5 +1,3 @@ --- Cavrealms plants and trees for lichen, moss and algae biomes - -- Lichen biome -- glowing fungi @@ -9,7 +7,7 @@ minetest.register_node("caverealms:fungus", { inventory_image = "caverealms_fungi.png", wield_image = "caverealms_fungi.png", is_ground_content = true, - groups = {oddly_breakable_by_hand = 3, attached_node = 1, flammable = 1}, + groups = {oddly_breakable_by_hand = 3, attached_node = 1}, light_source = 5, paramtype = "light", drawtype = "plantlike", @@ -29,7 +27,7 @@ minetest.register_node("caverealms:mycena", { inventory_image = "caverealms_mycena.png", wield_image = "caverealms_mycena.png", is_ground_content = true, - groups = {oddly_breakable_by_hand = 3, attached_node = 1, flammable = 1}, + groups = {oddly_breakable_by_hand = 3, attached_node = 1}, light_source = 6, paramtype = "light", drawtype = "plantlike", @@ -61,6 +59,7 @@ else tiles = {"caverealms_mushroom_cap.png"}, is_ground_content = true, groups = {choppy=2, oddly_breakable_by_hand=1,}, + light_source = 3, drop = { max_items = 1, items = { @@ -88,14 +87,62 @@ else }) end +-- green mushroom +-- cap +minetest.register_node("caverealms:mushroom_cap_green", { + description = "Giant Mushroom Cap, Green", + tiles = {"caverealms_mushroom_cap_green.png"}, + is_ground_content = true, + groups = {choppy=2, oddly_breakable_by_hand=1,}, + light_source = 3, + drop = { + max_items = 1, + items = { + {items = {"caverealms:mushroom_sapling_green"}, rarity = 20}, + {items = {"caverealms:mushroom_cap_green"}} + } + }, +}) +minetest.register_node("caverealms:mushroom_cap_green_spots", { + description = "Giant Mushroom Cap, Green", + tiles = {"caverealms_mushroom_cap_green.png^caverealms_mushroom_cap_spots.png"}, + is_ground_content = true, + groups = {choppy=2, oddly_breakable_by_hand=1,}, + light_source = 3, + drop = { + max_items = 1, + items = { + {items = {"caverealms:mushroom_sapling_green_spots"}, rarity = 20}, + {items = {"caverealms:mushroom_cap_green"}} + } + }, +}) + +-- sapling +minetest.register_node("caverealms:mushroom_sapling_green", { + description = "Mushroom Tree Sapling, Green", + drawtype = "plantlike", + tiles = {"caverealms_mushroom_sapling_green.png"}, + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + walkable = false, + selection_box = { + type = "fixed", + fixed = {-4 / 16, -0.5, -4 / 16, 4 / 16, 7 / 16, 4 / 16} + }, + groups = {snappy = 2, dig_immediate = 3, flammable = 2}, + sounds = default.node_sound_leaves_defaults(), +}) + + -- gills minetest.register_node("caverealms:mushroom_gills", { description = "Giant Mushroom Gills", tiles = {"caverealms_mushroom_gills.png"}, is_ground_content = true, light_source = 10, - walkable = false, - groups = {choppy=2, oddly_breakable_by_hand=1}, + groups = {choppy=2, oddly_breakable_by_hand=1}, drawtype = "plantlike", paramtype = "light", }) @@ -167,6 +214,45 @@ minetest.register_abm({ }) +-- green mushroom growth +function caverealms:grow_green_mushroom(x,y,z, area, data) + local c_stem = minetest.get_content_id("caverealms:mushroom_stem") + local c_gills = minetest.get_content_id("caverealms:mushroom_gills") + local c_cap = minetest.get_content_id("caverealms:mushroom_cap_green") + local c_caps = minetest.get_content_id("caverealms:mushroom_cap_green_spots") + + -- stem + local stop = {x=x,y=y+3,z=z}--{x = x+math.random(-1,1), y = y+3, z = z+math.random(-1,1)} + + for i = 1,3 do +-- local vi = area:index(x+((stop.x-x) / i), y+i, z+((stop.z-z) / i)) + local vi = area:index(x, y+i, z) + data[vi] = c_stem + end + + data[area:index(stop.x+1, y+3, stop.z)] = c_gills + data[area:index(stop.x-1, y+3, stop.z)] = c_gills + data[area:index(stop.x, y+3, stop.z+1)] = c_gills + data[area:index(stop.x, y+3, stop.z-1)] = c_gills + + data[area:index(stop.x+1, y+3, stop.z+1)] = c_cap + data[area:index(stop.x-1, y+3, stop.z+1)] = c_cap + data[area:index(stop.x+1, y+3, stop.z-1)] = c_cap + data[area:index(stop.x-1, y+3, stop.z-1)] = c_caps + data[area:index(stop.x+2, y+3, stop.z)] = c_cap + data[area:index(stop.x-2, y+3, stop.z)] = c_cap + data[area:index(stop.x, y+3, stop.z+2)] = c_caps + data[area:index(stop.x, y+3, stop.z-2)] = c_cap + data[area:index(stop.x+1, y+4, stop.z)] = c_caps + data[area:index(stop.x-1, y+4, stop.z)] = c_cap + data[area:index(stop.x, y+4, stop.z+1)] = c_caps + data[area:index(stop.x, y+4, stop.z-1)] = c_cap + data[area:index(stop.x, y+4, stop.z)] = c_cap + + +end + + -- spread moss/lichen/algae to nearby cobblestone minetest.register_abm({ label = "Caverealms stone spread", diff --git a/mods/caverealms/screenshot.png b/mods/caverealms/screenshot.png new file mode 100644 index 00000000..ad449457 Binary files /dev/null and b/mods/caverealms/screenshot.png differ diff --git a/mods/caverealms/textures/caverealms_blue_flame_animated.png b/mods/caverealms/textures/caverealms_blue_flame_animated.png new file mode 100644 index 00000000..2d4527f2 Binary files /dev/null and b/mods/caverealms/textures/caverealms_blue_flame_animated.png differ diff --git a/mods/caverealms/textures/caverealms_butterfly_blue.png b/mods/caverealms/textures/caverealms_butterfly_blue.png new file mode 100644 index 00000000..00616ad7 Binary files /dev/null and b/mods/caverealms/textures/caverealms_butterfly_blue.png differ diff --git a/mods/caverealms/textures/caverealms_butterfly_blue_animated.png b/mods/caverealms/textures/caverealms_butterfly_blue_animated.png new file mode 100644 index 00000000..82b85c1d Binary files /dev/null and b/mods/caverealms/textures/caverealms_butterfly_blue_animated.png differ diff --git a/mods/caverealms/textures/caverealms_glow_citrine.png b/mods/caverealms/textures/caverealms_glow_citrine.png new file mode 100644 index 00000000..afc6eb3a Binary files /dev/null and b/mods/caverealms/textures/caverealms_glow_citrine.png differ diff --git a/mods/caverealms/textures/caverealms_glow_citrine_ore.png b/mods/caverealms/textures/caverealms_glow_citrine_ore.png new file mode 100644 index 00000000..275c2f72 Binary files /dev/null and b/mods/caverealms/textures/caverealms_glow_citrine_ore.png differ diff --git a/mods/caverealms/textures/caverealms_glow_obsidian_shard.png b/mods/caverealms/textures/caverealms_glow_obsidian_shard.png new file mode 100644 index 00000000..b49e6ce1 Binary files /dev/null and b/mods/caverealms/textures/caverealms_glow_obsidian_shard.png differ diff --git a/mods/caverealms/textures/caverealms_glow_worm_red.png b/mods/caverealms/textures/caverealms_glow_worm_red.png new file mode 100644 index 00000000..bcb64997 Binary files /dev/null and b/mods/caverealms/textures/caverealms_glow_worm_red.png differ diff --git a/mods/caverealms/textures/caverealms_mushroom_cap_green.png b/mods/caverealms/textures/caverealms_mushroom_cap_green.png new file mode 100644 index 00000000..26f6d7fe Binary files /dev/null and b/mods/caverealms/textures/caverealms_mushroom_cap_green.png differ diff --git a/mods/caverealms/textures/caverealms_mushroom_cap_spots.png b/mods/caverealms/textures/caverealms_mushroom_cap_spots.png new file mode 100644 index 00000000..99760389 Binary files /dev/null and b/mods/caverealms/textures/caverealms_mushroom_cap_spots.png differ diff --git a/mods/caverealms/textures/caverealms_mushroom_sapling_green.png b/mods/caverealms/textures/caverealms_mushroom_sapling_green.png new file mode 100644 index 00000000..34c3aaff Binary files /dev/null and b/mods/caverealms/textures/caverealms_mushroom_sapling_green.png differ diff --git a/mods/hot_air_balloons/README.txt b/mods/hot_air_balloons/README.txt new file mode 100644 index 00000000..9ad2c173 --- /dev/null +++ b/mods/hot_air_balloons/README.txt @@ -0,0 +1,54 @@ +This mod adds craftable and ridable hot air balloons to minetest. +Controls: + right click with fuel item: increase heat and buoyancy (*) + right click without coal: enter or leave balloon + left, right, up, down (default WASD): accelerate the balloon + sneak (default shift): decrease heat, lowering buoyancy + jump (default space): turn the balloon towards where the player is looking + + + +optional dependencies: + default, bucket (enable crafting recipe 1 if installed together) + mcl_core, mcl_mobitems, mcl_buckets (enable crafting recipe 2 if installed together) + + +Crafting recipe 1 (Minetest Game and most derivatives): +[P] := paper +[W] := wood +[L] := lava bucket +[ ] := nothing + +[P][P][P] +[P][L][P] +[ ][W][ ] + +Crafting recipe 2 (MineClone 2): +[L] := leather +[W] := wood +[V] := lava bucket +[S] := string + +[L][L][L] +[L][V][L] +[S][W][S] + +See license.txt for proper license information. + +Author of code +---------------------------------------- +NetherEran (LGPL v2.1) + +Authors of media (models, textures) +---------------------------------------- +Textures +-------- +NetherEran (CC BY-SA 3.0): + hot_air_balloons_balloon.png + hot_air_balloons_balloon_flame.png + hot_air_balloons_balloon_model.png --Contains default_wood.png (by BlockMen) and default_aspen_wood.png (by sofar) (derived from default_pine_wood by paramat) + +Models +-------- +NetherEran (CC BY-SA 3.0): + ballon.blend (= hot_air_balloons_balloon.obj) diff --git a/mods/hot_air_balloons/absent_ballooner_rescuing.lua b/mods/hot_air_balloons/absent_ballooner_rescuing.lua new file mode 100644 index 00000000..b80c3e1a --- /dev/null +++ b/mods/hot_air_balloons/absent_ballooner_rescuing.lua @@ -0,0 +1,101 @@ +--localize things for better performance +local serialize = minetest.serialize +local add_entity = minetest.add_entity +local after = minetest.after + +--for storing which players left while in a balloon +local storage = minetest.get_mod_storage() +local absent_ballooners = minetest.deserialize(storage:get_string("absent_ballooners")) or {} + + +--putting leaving people into storage +local leave_while_ballooning = function(player) + local parent = player:get_attach() + if parent and not parent:is_player() + then + local balloon_type = parent:get_luaentity().balloon_type + if balloon_type + then + --remove() only works if someone else is in the area, + --hence the need for mark_for_deletion + parent:remove() + absent_ballooners[player:get_player_name()] = balloon_type + end + end +end + +--same as on_leave but for all players at once +local on_shutdown = function() + local connected_players = minetest.get_connected_players() + for i, p in ipairs(connected_players) + do + leave_while_ballooning(p) + end + storage:set_string("absent_ballooners", serialize(absent_ballooners)) +end +--putting leaving people into storage and saving storage +local on_leave = function(player) + leave_while_ballooning(player) + storage:set_string("absent_ballooners", serialize(absent_ballooners)) +end + +minetest.register_on_leaveplayer(on_leave) +minetest.register_on_shutdown(on_shutdown) + +--checking if player who joined was ballooning when they left +--if so spawn a new balloon and set them as attachment +local on_join = function(player) + if player + then + local name = player:get_player_name() + if absent_ballooners[name] + then + local pos = player:get_pos() + + --for compatibility with version 1.1 of the mod + if absent_ballooners[name] == true + then + absent_ballooners[name] = "hot_air_balloons:balloon" + end + --minetest doesn't seem to like add_entity on init so a minetest.after is used + --player is set as pilot in on_activate + after(2, + function() + --concatenating "P" with name signals that player should be set as attach + add_entity(pos, absent_ballooners[name], "P" .. name) + end) + end + end +end +minetest.register_on_joinplayer(on_join) + + +--called in on_activate if balloon was spawned to rescue an absent ballooner +local set_rescue = function(self, playername) + local player = minetest.get_player_by_name(playername) + self.pilot = playername + if not player --player logged off right away + then + self.object:remove() + return + end + player:set_attach(self.object, "", + {x = 0, y = 1, z = 0}, {x = 0, y = 0, z = 0}) + absent_ballooners[playername] = nil +end +--set as get_staticdata +local mark_for_deletion = function(self) + if self.pilot + then + --pilot logged off while ballooning, deleting balloon on next activation + return "R" + else + --normally save and load balloon + return "" + end +end + + +return set_rescue, mark_for_deletion + + diff --git a/mods/hot_air_balloons/api.txt b/mods/hot_air_balloons/api.txt new file mode 100644 index 00000000..97f1febf --- /dev/null +++ b/mods/hot_air_balloons/api.txt @@ -0,0 +1,30 @@ +API functions: + hot_air_balloons.get_entity(name, mesh_name, texture_name) + arguments: + 'name': string in the format "modname:entityname" + 'mesh_name': string in the format "modname_meshFileName" + 'texture_name': string in the format "modname_textureFileName" + Example usage: minetest.register_entity(hot_air_balloons.get_entity(foo, raa, see)) + you can also store the values in a local variable and change the fields of the entity definition table before registering the entity. + passing the entity name to the function is used when a balloon pilot logs off + + + + hot_air_balloons.get_item(name, description, texture, object_name) + arguments: + 'name': string in the format "modname:itemname" + 'description': string that appears in the tooltip. Use minetest.translate with this. + 'texture': string in the format "modname_textureFileName" + 'object_name' is the name specified in hot_air_balloons.get_entity + Example usage: minetest.register_craftitem(hot_air_balloons.get_item(foo, raa, see, mon)) + returns an item name and an item definition table + as with get_entity you can store the item definition table and change its fields before registering the item. + + + +explanation of the custom fields of the entitiy definition table: + pilot: stores the player name of the pilot or nil if there is no pilot + heat: integer in the interval [0, 12000) + Decides wether to fly up or down + balloon_type: entity name of the balloon, e.g."hot_air_balloons:balloon". + used to make the balloon log off and log back in together with its pilot \ No newline at end of file diff --git a/mods/hot_air_balloons/craft.lua b/mods/hot_air_balloons/craft.lua new file mode 100644 index 00000000..0d6742d6 --- /dev/null +++ b/mods/hot_air_balloons/craft.lua @@ -0,0 +1,45 @@ +--This file adds crafting recipes depending on which dependencies are installed + +if minetest.get_modpath("default") and minetest.get_modpath("bucket") then + minetest.register_craft({ + output = "hot_air_balloons:item", + recipe = { + { "animalia:leather", "animalia:leather", "animalia:leather" }, + { "animalia:leather", "bucket:bucket_lava", "animalia:leather" }, + { "farming:string", "group:wood", "farming:string" }, + }, + }) + return +end +if + minetest.get_modpath("mcl_buckets") + and minetest.get_modpath("mcl_mobitems") + and minetest.get_modpath("mcl_core") +then + minetest.register_craft({ + output = "hot_air_balloons:item", + recipe = { + { "mcl_mobitems:leather", "mcl_mobitems:leather", "mcl_mobitems:leather" }, + { "mcl_mobitems:leather", "bucket:bucket_lava", "mcl_mobitems:leather" }, + { "mcl_mobitems:string", "group:wood", "mcl_mobitems:string" }, + }, + }) + return +end +--[[ +minetest.register_craft( +{ + type = "aircraft" +} +]] + +--make balloon work with mcl2 creative mode + +--announce in chat if no crafting recipe was added. +minetest.after(2, function() + minetest.chat_send_all( + "Optional dependencies for hot_air_balloons are missing so no crafting recipe was added.\n" + .. "Install either 'default' and 'bucket' or 'mcl_core', 'mcl_mobitems' 'and mcl_buckets' if this bothers you.\n" + .. "All other functions of the mod should be unaffected by this." + ) +end) diff --git a/mods/hot_air_balloons/depends.txt b/mods/hot_air_balloons/depends.txt new file mode 100644 index 00000000..a2d05cb6 --- /dev/null +++ b/mods/hot_air_balloons/depends.txt @@ -0,0 +1,5 @@ +default? +bucket? +mcl_buckets? +mcl_mobitems? +mcl_core? \ No newline at end of file diff --git a/mods/hot_air_balloons/init.lua b/mods/hot_air_balloons/init.lua new file mode 100644 index 00000000..415721ab --- /dev/null +++ b/mods/hot_air_balloons/init.lua @@ -0,0 +1,226 @@ +--localize functions for better performance +local string_byte = string.byte +local string_sub = string.sub +local get_item_group = minetest.get_item_group +local add_particlespawner = minetest.add_particlespawner +local add_item = minetest.add_item +local get_node = minetest.get_node +local add_entity = minetest.add_entity + +local modpath = minetest.get_modpath("hot_air_balloons") +local set_rescue, mark_for_deletion_if_piloted = dofile(modpath .. "/absent_ballooner_rescuing.lua") +local handle_movement = dofile(modpath .. "/movement.lua") + +local is_in_creative = function(name) + return creative and creative.is_enabled_for + and creative.is_enabled_for(name) +end + +local get_fire_particle = function (pos) + pos.y = pos.y + 3 + return { + amount = 3, + time = 1, + minpos = pos, + maxpos = pos, + minvel = {x = 0, y = 1, z = 0}, + maxvel = {x = 0, y = 1, z = 0}, + minexptime = 1, + maxexptime = 1, + minsize = 10, + maxsize = 5, + collisiondetection = false, + vertical = false, + texture = "hot_air_balloons_flame.png" + } +end + +local function get_fuel_value(item) + input = { + method = "fuel", + items = {item}, + } + return minetest.get_craft_result(input).time or 0 +end + +local add_heat = function(self, player) + local item_stack = player:get_wielded_item() + local fuelval = get_fuel_value(item_stack) + if not fuelval or fuelval == 0 then + return false + end + local heat = self.heat + heat = heat + 30 * fuelval --for coal lump 1 min until heat is back to original + if heat < 12000 --cap heat at 12000 (10 min) + then + self.heat = heat + --adding particle effect + local pos = self.object:get_pos() + add_particlespawner(get_fire_particle(pos)) + if not is_in_creative(player:get_player_name()) + then + item_stack:take_item() + player:set_wielded_item(item_stack) + end + end + return true +end + +--global table, has fields get_entity_def and get_item_def +--custom balloons right now turn into normal ones when the pilot leaves +hot_air_balloons = {} +hot_air_balloons.get_entity = function(name, mesh_name, texture_name) + return + name, + { + initial_properties = + { + hp_max = 1, + physical = true, + weight = 5, + collisionbox = {-0.65, -0.01, -0.65, 0.65, 1.11, 0.65}, + visual = "mesh", + mesh = "hot_air_balloons_balloon.obj", + textures = {"hot_air_balloons_balloon_model.png"}, + is_visible = true, + makes_footstep_sound = false, + backface_culling = false, + }, + heat = 0, + balloon_type = name, + + on_step = function(self, dtime) + --decrease heat, move + if self.heat > 0 + then + self.heat = self.heat - 1 + end + handle_movement(self) + end, + on_rightclick = function (self, clicker) + --if hoding coal, increase heat, else mount/dismount + if not clicker or not clicker:is_player() + then + return + end + --checking if clicker is holding coal + --heating balloon and returning if yes + if add_heat(self, clicker) + then + return + end + + --if not holding coal: + local playername = clicker:get_player_name() + if self.pilot and self.pilot == playername + then + self.pilot = nil + clicker:set_detach() + elseif not self.pilot + then + --attach + self.pilot = playername + clicker:set_attach(self.object, "", + {x = 0, y = 1, z = 0}, {x = 0, y = 0, z = 0}) + end + end, + --if pilot leaves start sinking and prepare for next pilot + on_detach_child = function(self, child) + self.heat = 0 + self.object:setvelocity({x = 0, y = 0, z = 0}) + end, + + on_activate = function(self, staticdata, dtime_s) + self.object:set_armor_groups({punch_operable = 1}) + --so balloons don't get lost + self.object:setvelocity({x = 0, y = 0, z = 0}) + + --checking if balloon was spawned from item or unloaded without pilot + if staticdata == "" + then + return + end + --checking if balloon should despawn when pilot logged off + local first_char = string_byte(staticdata) + --ballooner logged off, balloon will respawn when ballooner logs back in + if first_char == 82 --chr 82 = R + then + self.object:remove() + return + --absent ballooner logged back in + elseif first_char == 80 --chr 80 = P + then + set_rescue(self, string_sub(staticdata, 2)) + end + end, + + get_staticdata = mark_for_deletion_if_piloted, + + + on_punch = function(self, puncher) --drop balloon item + if self.pilot + then + return + elseif not (puncher and puncher:is_player()) + then + return + else + self.object:remove() + local inv = puncher:get_inventory() + if not is_in_creative(puncher:get_player_name()) + or not inv:contains_item("main", "hot_air_balloons:item") + then + local leftover = inv:add_item("main", "hot_air_balloons:item") + if not leftover:is_empty() + then + add_item(self.object:get_pos(), leftover) + end + end + end + end, + } +end + +hot_air_balloons.get_item = function(name, description, texture, object_name) +return + name, + { + description = description, + inventory_image = texture, + stack_max = 1, + liquids_pointable = true, + groups = {flammable = 2}, + on_place = + function (itemstack, placer, pointed_thing) + --places balloon if the clicked thing is a node and the above node is air + if pointed_thing.type == "node" + and get_node (pointed_thing.above).name == "air" + then + if not is_in_creative(placer:get_player_name()) + then + itemstack:take_item() + end + local pos_to_place = pointed_thing.above + pos_to_place.y = pos_to_place.y - 0.5 --subtracting 0.5 to place on ground + add_entity(pointed_thing.above, object_name) + end + --add remaining items to inventory + return itemstack + end + } +end +--registering the balloon entity, item and recepies + +minetest.register_entity(hot_air_balloons.get_entity( + "hot_air_balloons:balloon", + "hot_air_balloons_balloon.obj", + "hot_air_balloons_balloon_model.png")) + +minetest.register_craftitem(hot_air_balloons.get_item( + "hot_air_balloons:item", + minetest.translate("hot_air_balloons", "Hot Air Balloon"), + "hot_air_balloons_balloon.png", + "hot_air_balloons:balloon")) + + +dofile(modpath .. "/craft.lua") diff --git a/mods/hot_air_balloons/license.txt b/mods/hot_air_balloons/license.txt new file mode 100644 index 00000000..8dc62085 --- /dev/null +++ b/mods/hot_air_balloons/license.txt @@ -0,0 +1,54 @@ +License of source code +---------------------- + +GNU Lesser General Public License, version 2.1 +Copyright (C) 2019 NetherEran + +This program is free software; you can redistribute it and/or modify it under the terms +of the GNU Lesser General Public License as published by the Free Software Foundation; +either version 2.1 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details: +https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + + +Licenses of media (textures, models and sounds) +----------------------------------------------- + +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) +Copyright (C) 2010-2018: + + NetherEran + BlockMen + sofar + paramat + +You are free to: +Share — copy and redistribute the material in any medium or format. +Adapt — remix, transform, and build upon the material for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and +indicate if changes were made. You may do so in any reasonable manner, but not in any way +that suggests the licensor endorses you or your use. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute +your contributions under the same license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that +legally restrict others from doing anything the license permits. + +Notices: + +You do not have to comply with the license for elements of the material in the public +domain or where your use is permitted by an applicable exception or limitation. +No warranties are given. The license may not give you all of the permissions necessary +for your intended use. For example, other rights such as publicity, privacy, or moral +rights may limit how you use the material. + +For more details: +http://creativecommons.org/licenses/by-sa/3.0/ \ No newline at end of file diff --git a/mods/hot_air_balloons/locale/hot_air_balloons.de.tr b/mods/hot_air_balloons/locale/hot_air_balloons.de.tr new file mode 100644 index 00000000..610cc415 --- /dev/null +++ b/mods/hot_air_balloons/locale/hot_air_balloons.de.tr @@ -0,0 +1,2 @@ +# textdomain:hot_air_balloons +Hot Air Balloon=Heißluftballon \ No newline at end of file diff --git a/mods/hot_air_balloons/locale/hot_air_balloons.es.tr b/mods/hot_air_balloons/locale/hot_air_balloons.es.tr new file mode 100644 index 00000000..05f331ed --- /dev/null +++ b/mods/hot_air_balloons/locale/hot_air_balloons.es.tr @@ -0,0 +1,2 @@ +# textdomain:hot_air_balloons +Hot Air Balloon=Globo Aerostático \ No newline at end of file diff --git a/mods/hot_air_balloons/locale/hot_air_balloons.ro.tr b/mods/hot_air_balloons/locale/hot_air_balloons.ro.tr new file mode 100644 index 00000000..99cf77d3 --- /dev/null +++ b/mods/hot_air_balloons/locale/hot_air_balloons.ro.tr @@ -0,0 +1,2 @@ +# textdomain:hot_air_balloons +Hot Air Balloon=Balon cu aer cald \ No newline at end of file diff --git a/mods/hot_air_balloons/mod.conf b/mods/hot_air_balloons/mod.conf new file mode 100644 index 00000000..59c0edc3 --- /dev/null +++ b/mods/hot_air_balloons/mod.conf @@ -0,0 +1,6 @@ +name = hot_air_balloons +author = Eran +description = Adds craftable and controllable hot air balloons. +optional_depends = default, bucket, mcl_buckets, mcl_mobitems, mcl_core +release = 15478 +title = Hot Air Balloons diff --git a/mods/hot_air_balloons/models/balloon.blend b/mods/hot_air_balloons/models/balloon.blend new file mode 100644 index 00000000..68cecf6c Binary files /dev/null and b/mods/hot_air_balloons/models/balloon.blend differ diff --git a/mods/hot_air_balloons/models/hot_air_balloons_balloon.obj b/mods/hot_air_balloons/models/hot_air_balloons_balloon.obj new file mode 100644 index 00000000..871dada6 --- /dev/null +++ b/mods/hot_air_balloons/models/hot_air_balloons_balloon.obj @@ -0,0 +1,485 @@ +# Blender v2.79 (sub 0) OBJ File: 'balloon.blend' +# www.blender.org +mtllib hot_air_balloons_balloon.mtl +o Cube.001 +v -19.500000 44.216793 19.500000 +v -19.500000 83.216797 19.500000 +v -19.500000 44.216793 -19.500000 +v -19.500000 83.216797 -19.500000 +v 19.500000 44.216793 19.500000 +v 19.500000 83.216797 19.500000 +v 19.500000 44.216793 -19.500000 +v 19.500000 83.216797 -19.500000 +v -6.500000 0.016788 6.500000 +v 6.500000 0.016788 6.500000 +v -6.500000 0.016788 -6.500000 +v 6.500000 0.016788 -6.500000 +v -6.500000 1.441189 6.500000 +v 6.500000 1.441189 6.500000 +v -6.500000 1.441189 -6.500000 +v 6.500000 1.441189 -6.500000 +v -6.500000 0.016788 -4.550000 +v -6.500000 0.016788 4.550000 +v 6.500000 0.016788 4.550000 +v 6.500000 0.016788 -4.550000 +v -6.500000 1.441189 -4.550000 +v -6.500000 1.441189 4.550000 +v 6.500000 1.441189 4.550000 +v 6.500000 1.441189 -4.550000 +v -4.550000 0.016788 6.500000 +v 4.550000 0.016788 6.500000 +v 4.550000 0.016788 -6.500000 +v -4.550000 0.016788 -6.500000 +v -4.550000 1.441189 6.500000 +v 4.550000 1.441189 6.500000 +v 4.550000 1.441189 -6.500000 +v -4.550000 1.441189 -6.500000 +v 4.550000 1.441189 4.550000 +v -4.550000 1.441189 4.550000 +v -4.550000 1.441189 -4.550000 +v -4.550000 0.016788 4.550000 +v 4.550000 0.016788 4.550000 +v -4.550000 0.016788 -4.550000 +v 4.550000 0.016788 -4.550000 +v 6.500000 11.077995 6.500000 +v -6.500000 11.077995 6.500000 +v 6.500000 11.077995 6.500000 +v -6.500000 11.077995 -6.500000 +v -6.500000 11.077995 6.500000 +v 6.500000 11.077995 -6.500000 +v 4.550000 11.077995 4.550000 +v 4.550000 11.077995 -4.550000 +v -4.550000 11.077995 -4.550000 +v -4.550000 11.077995 -4.550000 +v -4.550000 1.441189 -4.550000 +v 4.550000 1.441189 -4.550000 +v 4.550000 11.077995 -4.550000 +v -4.550000 11.077995 4.550000 +v 4.550000 1.441189 4.550000 +v 6.500000 1.441189 -6.500000 +v 4.550000 11.077995 4.550000 +v -4.550000 11.077995 -6.500000 +v -6.500000 1.441189 -6.500000 +v 4.550000 11.077995 -6.500000 +v 4.550000 11.077995 6.500000 +v -4.550000 11.077995 6.500000 +v 6.500000 11.077995 -4.550000 +v 6.500000 1.441189 6.500000 +v 6.500000 11.077995 4.550000 +v -6.500000 11.077995 4.550000 +v -6.500000 11.077995 -4.550000 +v -6.500000 1.441189 6.500000 +v 6.500000 11.077995 -6.500000 +v -6.500000 11.077995 -6.500000 +v 5.586319 8.609333 -5.559702 +v 11.988016 44.915165 -12.060156 +v 5.537368 8.331722 -7.158404 +v 11.939065 44.637550 -13.658857 +v 7.185020 8.327442 -5.559702 +v 13.586718 44.633270 -12.060156 +v 7.136070 8.049831 -7.158404 +v 13.537767 44.355656 -13.658857 +v -5.586318 8.609333 -5.559702 +v -11.988014 44.915165 -12.060155 +v -5.537367 8.331722 -7.158404 +v -11.939065 44.637554 -13.658857 +v -7.185020 8.327442 -5.559702 +v -13.586717 44.633270 -12.060155 +v -7.136071 8.049831 -7.158404 +v -13.537764 44.355656 -13.658857 +v 5.586319 8.609337 5.559702 +v 11.988015 44.915161 12.060157 +v 5.537368 8.331722 7.158404 +v 11.939065 44.637547 13.658860 +v 7.185020 8.327442 5.559702 +v 13.586717 44.633270 12.060157 +v 7.136070 8.049831 7.158404 +v 13.537766 44.355652 13.658860 +v -5.586318 8.609337 5.559702 +v -11.988016 44.915161 12.060155 +v -5.537367 8.331722 7.158405 +v -11.939065 44.637550 13.658857 +v -7.185019 8.327442 5.559704 +v -13.586717 44.633266 12.060157 +v -7.136071 8.049831 7.158405 +v -13.537766 44.355652 13.658860 +vt 0.666667 0.666667 +vt 0.333333 0.666667 +vt 0.333333 0.333333 +vt 0.666667 0.333333 +vt 0.000000 1.000000 +vt 0.000000 0.666667 +vt 0.333333 0.666667 +vt 0.333333 1.000000 +vt 0.333333 0.333333 +vt 0.000000 0.333333 +vt 0.000000 0.000000 +vt 0.333333 0.000000 +vt 0.333333 0.333333 +vt 0.333333 0.000000 +vt 0.666667 0.000000 +vt 0.666667 0.333333 +vt 0.000000 0.666667 +vt 0.000000 0.333333 +vt 0.333333 0.666667 +vt 0.666667 0.000000 +vt 1.000000 0.000000 +vt 1.000000 0.333333 +vt 0.666667 0.333333 +vt 0.350615 0.683822 +vt 0.333949 0.683822 +vt 0.333949 0.667155 +vt 0.350615 0.667155 +vt 0.761207 0.649225 +vt 0.761207 0.665892 +vt 0.749033 0.665892 +vt 0.749033 0.649225 +vt 0.761207 0.427778 +vt 0.761207 0.444444 +vt 0.749033 0.444444 +vt 0.749033 0.427778 +vt 0.538985 0.761111 +vt 0.538985 0.777778 +vt 0.526810 0.777778 +vt 0.526810 0.761111 +vt 0.667442 0.461111 +vt 0.667442 0.444444 +vt 0.679616 0.444444 +vt 0.679616 0.461111 +vt 0.667442 0.555556 +vt 0.667442 0.538889 +vt 0.679616 0.538889 +vt 0.679616 0.555556 +vt 0.761207 0.555556 +vt 0.761207 0.572222 +vt 0.749033 0.572222 +vt 0.749033 0.555556 +vt 0.761207 0.650000 +vt 0.749033 0.650000 +vt 0.749420 0.427778 +vt 0.749420 0.444444 +vt 0.667054 0.444444 +vt 0.667054 0.427778 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.445060 0.683822 +vt 0.428393 0.683822 +vt 0.428393 0.667155 +vt 0.445060 0.667155 +vt 0.428393 0.777778 +vt 0.350615 0.777778 +vt 0.350615 0.761111 +vt 0.428393 0.761111 +vt 0.350615 0.683333 +vt 0.428393 0.683333 +vt 0.445060 0.777778 +vt 0.445060 0.761111 +vt 0.445060 0.683333 +vt 0.749420 0.555556 +vt 0.749420 0.572222 +vt 0.667054 0.572222 +vt 0.667054 0.555556 +vt 0.616763 0.749521 +vt 0.538985 0.749521 +vt 0.538985 0.667155 +vt 0.616763 0.667155 +vt 0.750035 0.334724 +vt 0.750035 0.351390 +vt 0.667669 0.351390 +vt 0.667669 0.334724 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.538985 0.667362 +vt 0.538985 0.684029 +vt 0.526810 0.684029 +vt 0.526810 0.667362 +vt 0.538985 0.683333 +vt 0.526810 0.683333 +vt 0.761822 0.334724 +vt 0.761822 0.351390 +vt 0.749648 0.351390 +vt 0.749648 0.334724 +vt 0.761207 0.350000 +vt 0.749033 0.350000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.333949 0.777778 +vt 0.333949 0.761111 +vt 0.333949 0.683333 +vt 0.744444 0.749033 +vt 0.666667 0.749033 +vt 0.666667 0.666667 +vt 0.744444 0.666667 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.744444 0.666667 +vt 0.822222 0.666667 +vt 0.822222 0.749033 +vt 0.744444 0.749033 +vt 0.444445 0.777778 +vt 0.444445 0.761111 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.616763 0.777778 +vt 0.616763 0.666667 +vt 0.633429 0.666667 +vt 0.633429 0.777778 +vt 0.616763 0.831398 +vt 0.538985 0.831398 +vt 0.538985 0.749033 +vt 0.616763 0.749033 +vt 0.444445 0.683333 +vt 0.749420 0.650000 +vt 0.667054 0.650000 +vt 0.678841 0.538889 +vt 0.678841 0.461111 +vt 0.761207 0.461111 +vt 0.761207 0.538889 +vt 0.749420 0.649225 +vt 0.749420 0.665892 +vt 0.667054 0.665892 +vt 0.667054 0.649225 +vt 0.678841 0.555556 +vt 0.761207 0.555556 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.678841 0.444444 +vt 0.761207 0.444444 +vt 0.749420 0.350000 +vt 0.667054 0.350000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.444445 0.684029 +vt 0.444445 0.667362 +vt 0.633429 0.666667 +vt 0.650096 0.666667 +vt 0.650096 0.777778 +vt 0.633429 0.777778 +vt 0.744444 0.749033 +vt 0.761111 0.749033 +vt 0.761111 0.826810 +vt 0.744444 0.826810 +vt 0.777778 0.749033 +vt 0.777778 0.826810 +vt 0.761111 0.826810 +vt 0.761111 0.749033 +vt 0.666667 0.749033 +vt 0.744444 0.749033 +vt 0.744444 0.826810 +vt 0.666667 0.826810 +vt 0.858286 0.648647 +vt 0.858286 0.333406 +vt 0.872154 0.333333 +vt 0.872154 0.648574 +vt 0.941529 0.333333 +vt 0.941529 0.648427 +vt 0.927654 0.648427 +vt 0.927654 0.333333 +vt 0.844417 0.333406 +vt 0.844417 0.648647 +vt 0.830549 0.648574 +vt 0.830549 0.333333 +vt 0.955404 0.333333 +vt 0.955404 0.648427 +vt 0.941529 0.648427 +vt 0.941529 0.333333 +vt 0.650096 0.694844 +vt 0.663754 0.695262 +vt 0.663754 0.708933 +vt 0.650096 0.708515 +vt 0.663754 0.722603 +vt 0.650096 0.723022 +vt 0.650096 0.709351 +vt 0.663754 0.708933 +vt 0.844417 0.333333 +vt 0.858286 0.333406 +vt 0.858286 0.648647 +vt 0.844417 0.648574 +vt 0.913779 0.333333 +vt 0.927654 0.333333 +vt 0.927654 0.648427 +vt 0.913779 0.648427 +vt 0.830549 0.648574 +vt 0.816681 0.648647 +vt 0.816681 0.333406 +vt 0.830549 0.333333 +vt 0.886029 0.648427 +vt 0.872154 0.648427 +vt 0.872154 0.333333 +vt 0.886029 0.333333 +vt 0.650096 0.694844 +vt 0.650096 0.681174 +vt 0.663754 0.680755 +vt 0.663754 0.694426 +vt 0.650096 0.750781 +vt 0.650096 0.737110 +vt 0.663754 0.737528 +vt 0.663754 0.751199 +vt 0.802812 0.648647 +vt 0.788944 0.648574 +vt 0.788944 0.333333 +vt 0.802812 0.333406 +vt 0.886029 0.648427 +vt 0.899904 0.648427 +vt 0.899904 0.963520 +vt 0.886029 0.963520 +vt 0.762184 0.333406 +vt 0.776052 0.333333 +vt 0.776052 0.648574 +vt 0.762183 0.648647 +vt 0.913779 0.648426 +vt 0.899904 0.648427 +vt 0.899904 0.333333 +vt 0.913779 0.333333 +vt 0.650096 0.765288 +vt 0.650096 0.751617 +vt 0.663754 0.751199 +vt 0.663754 0.764869 +vt 0.650096 0.680337 +vt 0.650096 0.666667 +vt 0.663754 0.667085 +vt 0.663754 0.680755 +vt 0.816681 0.333333 +vt 0.816681 0.648574 +vt 0.802812 0.648647 +vt 0.802812 0.333406 +vt 0.886029 0.648427 +vt 0.886029 0.333333 +vt 0.899904 0.333333 +vt 0.899904 0.648426 +vt 0.788944 0.333406 +vt 0.788944 0.648647 +vt 0.775075 0.648574 +vt 0.775075 0.333333 +vt 0.872154 0.963520 +vt 0.872154 0.648427 +vt 0.886029 0.648427 +vt 0.886029 0.963520 +vt 0.650096 0.723022 +vt 0.663754 0.723440 +vt 0.663754 0.737110 +vt 0.650096 0.736692 +vt 0.774865 0.662317 +vt 0.761207 0.662735 +vt 0.761207 0.649065 +vt 0.774865 0.648647 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn -0.9848 0.1736 0.0000 +vn -0.0302 -0.1710 -0.9848 +vn 0.9848 -0.1736 0.0000 +vn 0.0302 0.1710 0.9848 +vn -0.1710 -0.9698 0.1736 +vn 0.1710 0.9698 -0.1736 +vn 0.9848 0.1736 -0.0000 +vn 0.0302 -0.1710 -0.9848 +vn -0.9848 -0.1736 -0.0000 +vn -0.0302 0.1710 0.9848 +vn 0.1710 -0.9698 0.1736 +vn -0.1710 0.9698 -0.1737 +vn -0.0302 -0.1710 0.9848 +vn 0.0302 0.1710 -0.9848 +vn -0.1710 -0.9698 -0.1736 +vn 0.1710 0.9698 0.1737 +vn 0.0302 -0.1710 0.9848 +vn -0.0302 0.1710 -0.9848 +vn 0.1710 -0.9698 -0.1736 +vn -0.1710 0.9698 0.1736 +usemtl None +s off +f 1/1/1 2/2/1 4/3/1 3/4/1 +f 3/5/2 4/6/2 8/7/2 7/8/2 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/17/5 7/18/5 5/13/5 1/19/5 +f 8/20/6 4/21/6 2/22/6 6/23/6 +f 39/24/5 27/25/5 12/26/5 20/27/5 +f 18/28/1 9/29/1 13/30/1 22/31/1 +f 28/32/2 11/33/2 15/34/2 32/35/2 +f 26/36/4 10/37/4 14/38/4 30/39/4 +f 20/40/3 12/41/3 16/42/3 24/43/3 +f 10/44/3 19/45/3 23/46/3 14/47/3 +f 19/45/3 20/40/3 24/43/3 23/46/3 +f 11/48/1 17/49/1 21/50/1 15/51/1 +f 17/49/1 18/52/1 22/53/1 21/50/1 +f 32/54/2 15/55/2 69/56/2 57/57/2 +f 15/58/4 58/59/4 43/60/4 69/61/4 +f 26/62/5 37/63/5 19/64/5 10/65/5 +f 37/63/5 39/24/5 20/27/5 19/64/5 +f 18/66/5 17/67/5 38/68/5 36/69/5 +f 36/69/5 38/68/5 39/70/5 37/71/5 +f 9/72/5 18/66/5 36/69/5 25/73/5 +f 25/73/5 36/69/5 37/71/5 26/74/5 +f 15/75/1 21/76/1 66/77/1 69/78/1 +f 51/79/2 50/80/2 48/81/2 47/82/2 +f 16/83/2 31/84/2 59/85/2 68/86/2 +f 52/87/4 56/88/4 46/89/4 47/90/4 +f 9/91/4 25/92/4 29/93/4 13/94/4 +f 25/95/4 26/36/4 30/39/4 29/96/4 +f 12/97/2 27/98/2 31/99/2 16/100/2 +f 27/101/2 28/32/2 32/35/2 31/102/2 +f 49/103/4 52/87/4 47/90/4 48/104/4 +f 17/67/5 11/105/5 28/106/5 38/68/5 +f 38/68/5 28/106/5 27/107/5 39/70/5 +f 34/108/4 33/109/4 56/110/4 53/111/4 +f 13/112/4 67/113/4 41/114/4 44/115/4 +f 16/116/4 55/117/4 45/118/4 68/119/4 +f 54/120/3 51/121/3 47/122/3 46/123/3 +f 30/39/4 14/38/4 40/124/4 60/125/4 +f 14/126/4 63/127/4 42/128/4 40/129/4 +f 44/130/6 42/131/6 64/132/6 65/133/6 +f 35/134/1 34/135/1 53/136/1 49/137/1 +f 29/96/4 30/39/4 60/125/4 61/138/4 +f 21/76/1 22/139/1 65/140/1 66/77/1 +f 23/141/3 24/142/3 62/143/3 64/144/3 +f 22/145/1 13/146/1 44/147/1 65/148/1 +f 14/149/3 23/141/3 64/144/3 40/150/3 +f 50/151/4 35/152/4 49/103/4 48/104/4 +f 24/142/3 16/153/3 68/154/3 62/143/3 +f 31/155/2 32/54/2 57/57/2 59/156/2 +f 33/157/4 54/158/4 46/89/4 56/88/4 +f 13/94/4 29/93/4 61/159/4 44/160/4 +f 62/161/6 68/162/6 69/163/6 66/164/6 +f 62/165/6 52/166/6 56/167/6 64/168/6 +f 66/169/6 65/170/6 53/171/6 49/172/6 +f 51/173/5 54/174/5 34/175/5 50/176/5 +f 70/177/7 71/178/7 73/179/7 72/180/7 +f 72/181/8 73/182/8 77/183/8 76/184/8 +f 76/185/9 77/186/9 75/187/9 74/188/9 +f 74/189/10 75/190/10 71/191/10 70/192/10 +f 72/193/11 76/194/11 74/195/11 70/196/11 +f 77/197/12 73/198/12 71/199/12 75/200/12 +f 78/201/13 80/202/13 81/203/13 79/204/13 +f 80/205/14 84/206/14 85/207/14 81/208/14 +f 84/209/15 82/210/15 83/211/15 85/212/15 +f 82/213/16 78/214/16 79/215/16 83/216/16 +f 80/217/17 78/218/17 82/219/17 84/220/17 +f 85/221/18 83/222/18 79/223/18 81/224/18 +f 86/225/7 88/226/7 89/227/7 87/228/7 +f 88/229/19 92/230/19 93/231/19 89/232/19 +f 92/233/9 90/234/9 91/235/9 93/236/9 +f 90/237/20 86/238/20 87/239/20 91/240/20 +f 88/241/21 86/242/21 90/243/21 92/244/21 +f 93/245/22 91/246/22 87/247/22 89/248/22 +f 94/249/13 95/250/13 97/251/13 96/252/13 +f 96/253/23 97/254/23 101/255/23 100/256/23 +f 100/257/15 101/258/15 99/259/15 98/260/15 +f 98/261/24 99/262/24 95/263/24 94/264/24 +f 96/265/25 100/266/25 98/267/25 94/268/25 +f 101/269/26 97/270/26 95/271/26 99/272/26 diff --git a/mods/hot_air_balloons/movement.lua b/mods/hot_air_balloons/movement.lua new file mode 100644 index 00000000..1db168b0 --- /dev/null +++ b/mods/hot_air_balloons/movement.lua @@ -0,0 +1,209 @@ +--localize global functions +local vector_new = vector.new +local math_hypot = math.hypot +local atan = math.atan +local cos = math.cos +local sin = math.sin +local abs = math.abs +local pi = math.pi +local min = math.min + +--max speed settings +local max_ballooning_vertical_speed = 1 +local max_ballooning_horizontal_speed = 3 + +local function is_water_is_air(pos) + local node_name = minetest.get_node(pos).name + return minetest.get_item_group(node_name, "water") > 0, + node_name == "air" +end +local function get_vertical_acceleration(self) + local heat = self.heat + local vel_y = self.object:getvelocity().y + local acc_candidate = heat / 1000 - 0.5 + + --enforce max speed + if vel_y > max_ballooning_vertical_speed + and acc_candidate * vel_y > 0 + then + return 0 + else + return acc_candidate + end +end + +--if balloon is submerged +local function float_up(self, vel) + self.submerged = true + vel.y = 1 + return vel +end + +local function swim(self, vel) + --allow controls, allow up + local pos = self.object:get_pos() + --keep y constant or rising + local acc_y = get_vertical_acceleration(self) + + if self.submerged or acc_y < 0 + then + self.submerged = false + vel.y = 0 + return 0, vel + else + return acc_y, vel + end +end + + +local tau = pi * 2 +--returns the radian equivalent of a in the range [0, tau) +local function to_radian(a) + if a < 0 + then + return to_radian(a + tau) + elseif a >= tau + then + return to_radian(a - tau) + else + return a + end +end +--decides which is the shortest way to rotate towards where the player is looking +local function get_rotate_direction(a, b) + return to_radian(a - b) < to_radian(b - a) +end + +--rotates the balloon towards where the player is looking +local pi_192ths = pi / 192 --radians to turn each step +local function rotate(self, player) + -- + pi so it finishes rotating when looking towards where the player is looking + local player_yaw = player:get_look_horizontal() + pi + local self_yaw = self.object:getyaw() + + if get_rotate_direction(player_yaw, self_yaw) + then + self.object:setyaw(to_radian(self_yaw - pi_192ths)) + else + self.object:setyaw(to_radian(self_yaw + pi_192ths)) + end +end + +--takes wasd and turns it into a 2d vector +local pi_halves = pi / 2 +function get_direction(right, left, up, down) + local inline, cross = 0, 0 + local move = right or left or up or down + if left then cross = 1 end + if right then cross = cross - 1 end + if up then inline = 1 end + if down then inline = inline - 1 end + local arg + if inline < 0 + then + return atan(cross / inline) + pi, move + elseif inline > 0 + then + return atan(cross / inline), move + else + return pi_halves * cross, move + end +end + + +--[[ +space to rotate where the player is looking +wasd to apply acceleration +shift to let out hot air, cooling the balloon +]] +local function handle_control(self, vel) + if not self.pilot + then + return 0, 0 + end + local player = minetest.get_player_by_name(self.pilot) + if not player --player left, balloon should get deleted + then + return 0, 0 + end + local control = player:get_player_control() + if control.sneak --lowering heat quickly + then + local heat = self.heat - 30 + if heat < 0 + then + self.heat = 0 + else + self.heat = heat + end + end + + if control.jump --rotate towards player yaw + then + rotate(self, player) + end + + --taking direction from get_direction + --and turning it into radians. + --if max speed is reached, only acceleration in the opposite direction is applied. + local dir_radians, move = get_direction(control.right, control.left, control.up, control.down) + if move and math_hypot(vel.x, vel.z) + then + dir_radians = dir_radians + player:get_look_horizontal() + local x, z = -sin(dir_radians), cos(dir_radians) + if math_hypot(vel.x, vel.z) > max_ballooning_horizontal_speed + then + if x * vel.x > 0 + then + x = 0 + end + if z * vel.z > 0 + then + z = 0 + end + end + return x, z + end + return 0, 0 +end + +--[[handle movement in different cases +movement restrictions: + -on ground: only vertical movement + -on water: free movement, though vertical only if up + -submerged: free movement, vertical goes up automatically + -in air: completely free movement +]] + +return function(self) + local pos_in = self.object:get_pos() + local pos_under = vector_new(pos_in.x, pos_in.y - 0.1, pos_in.z) + local on_water, in_air = is_water_is_air(pos_under) + local acc = vector_new(0, 0, 0) + local vel = self.object:getvelocity() + + + if is_water_is_air(pos_in) --if submerged + then + vel = float_up(self, vel) + acc.x, acc.z = handle_control(self, vel) + self.object:setvelocity(vel) + elseif on_water --if on water + then + acc.y, vel = swim(self, vel) + self.object:setvelocity(vel) + acc.x, acc.z = handle_control(self, vel) + elseif in_air + then + --allow controls and height change + acc.y = get_vertical_acceleration(self) + acc.x, acc.z = handle_control(self, vel) + else --if on ground + --only allow height change + acc.y = get_vertical_acceleration(self) + vel.x = 0 + vel.z = 0 + self.object:setvelocity(vel) + end + self.object:setacceleration(acc) +end \ No newline at end of file diff --git a/mods/hot_air_balloons/screenshot.png b/mods/hot_air_balloons/screenshot.png new file mode 100644 index 00000000..f1395b2b Binary files /dev/null and b/mods/hot_air_balloons/screenshot.png differ diff --git a/mods/hot_air_balloons/textures/hot_air_balloons_balloon.png b/mods/hot_air_balloons/textures/hot_air_balloons_balloon.png new file mode 100644 index 00000000..2dec7b1c Binary files /dev/null and b/mods/hot_air_balloons/textures/hot_air_balloons_balloon.png differ diff --git a/mods/hot_air_balloons/textures/hot_air_balloons_balloon_model.png b/mods/hot_air_balloons/textures/hot_air_balloons_balloon_model.png new file mode 100644 index 00000000..6a0a6bae Binary files /dev/null and b/mods/hot_air_balloons/textures/hot_air_balloons_balloon_model.png differ diff --git a/mods/hot_air_balloons/textures/hot_air_balloons_flame.png b/mods/hot_air_balloons/textures/hot_air_balloons_flame.png new file mode 100644 index 00000000..658fbe58 Binary files /dev/null and b/mods/hot_air_balloons/textures/hot_air_balloons_flame.png differ diff --git a/mods/hud_compass/README.md b/mods/hud_compass/README.md new file mode 100644 index 00000000..474c3bd6 --- /dev/null +++ b/mods/hud_compass/README.md @@ -0,0 +1,64 @@ +HUD Compass [hud_compass] +------------------------- + +A Minetest mod to optionally place a HUD compass and a 24-hour clock on the screen. + +By David G (kestral246) + +![HUD Compass Screenshot](screenshot.png "hud_compass") + +How to enable +------------- + +This mod defaults to not displaying a compass or a clock. To enable, use the chat command: + + "/compass" -> By default this places just a compass in the bottom right corner of the screen. + +Repeated use of this command will toggle the display of the compass off and on. + +When given with an argument, the user can select whether to display just a compass, or a compass and a clock, and which corner of the screen to place them in. This is particularly useful with Android clients, where the bottom right corner of the screen has the jump button. + + "/compass 1" -> compass only, top right corner + "/compass 2" -> compass only, bottom right corner + "/compass 3" -> compass only, bottom left corner + "/compass 4" -> compass only, top left corner + + "/compass 5" -> compass and clock, top right corner + "/compass 6" -> compass and clock, bottom right corner + "/compass 7" -> compass and clock, bottom left corner + "/compass 8" -> compass and clock, top left corner + +In addition: + + "/compass 0" -> forces compass and clock off. + +**Note:** The clock is a 24-hour clock, with only one hand that displays the hours. Noon is at the top and midnight is at the bottom. + +**New:** The default initial state can now be set in minetest.conf using "compass\_default\_corner = n", where "n" can be one of the eight corner numbers above. If it's positive, the compass (or compass and clock) will be enabled at start, while if it's negative, they will start disabled. + +Local mod storage is used to maintain the state and position of hud_compass display between sessions, per user. + + +Special Thanks +-------------- +Special thanks go out to MoNTE48, for pointing out that the image files could be significantly reduced with zopflipng (much better than with optipng), in this mod's first Pull Request. + + +Licenses +-------- +Source code + +> The MIT License (MIT) + +Media (textures) + +> Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) + +> (Compass textures were copied from my realcompass mod, which were originally based on the textures created by tacotexmex for the ccompass mod. Clock textures were derived from these same textures.) + + + + + + + diff --git a/mods/hud_compass/init.lua b/mods/hud_compass/init.lua new file mode 100644 index 00000000..77337215 --- /dev/null +++ b/mods/hud_compass/init.lua @@ -0,0 +1,145 @@ +-- hud_compass +-- Optionally place a compass and 24-hour clock on the screen. +-- A HUD version of my realcompass mod. +-- By David_G (kestral246@gmail.com) +-- 2019-12-31 + +local hud_compass = {} +local storage = minetest.get_mod_storage() + +-- State of hud_compass +-- 1 = NE, 2 = SE, 3 = SW, 4 = NW (just compass) +-- 5 = NE, 6 = SE, 7 = SW, 8 = NW (both compass and clock) +-- positive == enabled, negative == disabled + +-- Define default (if not overridden): SE corner, compass only, off by default +local default_corner = tonumber(minetest.settings:get("compass_default_corner") or -2) + +local lookup_compass = { + {type="image", text="", position={x=1,y=0}, scale={x=4,y=4}, alignment={x=-1,y=1}, offset={x=-8,y=4}}, + {type="image", text="", position={x=1,y=1}, scale={x=4,y=4}, alignment={x=-1,y=-1}, offset={x=-8,y=-4}}, + {type="image", text="", position={x=0,y=1}, scale={x=4,y=4}, alignment={x=1,y=-1}, offset={x=8,y=-4}}, + {type="image", text="", position={x=0,y=0}, scale={x=4,y=4}, alignment={x=1,y=1}, offset={x=8,y=4}}, + {type="image", text="", position={x=1,y=0}, scale={x=4,y=4}, alignment={x=-1,y=1}, offset={x=-76,y=4}}, + {type="image", text="", position={x=1,y=1}, scale={x=4,y=4}, alignment={x=-1,y=-1}, offset={x=-76,y=-4}}, + {type="image", text="", position={x=0,y=1}, scale={x=4,y=4}, alignment={x=1,y=-1}, offset={x=76,y=-4}}, + {type="image", text="", position={x=0,y=0}, scale={x=4,y=4}, alignment={x=1,y=1}, offset={x=76,y=4}} +} +local lookup_clock = { + {type="image", text="", position={x=1,y=0}, scale={x=4,y=4}, alignment={x=-1,y=1}, offset={x=-8,y=4}}, + {type="image", text="", position={x=1,y=1}, scale={x=4,y=4}, alignment={x=-1,y=-1}, offset={x=-8,y=-4}}, + {type="image", text="", position={x=0,y=1}, scale={x=4,y=4}, alignment={x=1,y=-1}, offset={x=8,y=-4}}, + {type="image", text="", position={x=0,y=0}, scale={x=4,y=4}, alignment={x=1,y=1}, offset={x=8,y=4}}, + {type="image", text="", position={x=1,y=0}, scale={x=4,y=4}, alignment={x=-1,y=1}, offset={x=-8,y=4}}, + {type="image", text="", position={x=1,y=1}, scale={x=4,y=4}, alignment={x=-1,y=-1}, offset={x=-8,y=-4}}, + {type="image", text="", position={x=0,y=1}, scale={x=4,y=4}, alignment={x=1,y=-1}, offset={x=8,y=-4}}, + {type="image", text="", position={x=0,y=0}, scale={x=4,y=4}, alignment={x=1,y=1}, offset={x=8,y=4}} +} + +-- backward compatibility (core version < 5.9.0) +if not core.has_feature("hud_def_type_field") then + for _, t in pairs({lookup_compass, lookup_clock}) do + for _, def in pairs(t) do + def.hud_elem_type = def.type + def.type = nil + end + end +end + +minetest.register_on_joinplayer(function(player) + local pname = player:get_player_name() + local corner = default_corner + if storage:get(pname) and tonumber(storage:get(pname)) then -- validate mod storage value + local temp = math.floor(tonumber(storage:get(pname))) + if temp ~= nil and temp ~= 0 and temp >= -8 and temp <= 8 then + corner = temp + end + end + hud_compass[pname] = { + id_compass = player:hud_add(lookup_compass[math.abs(corner)]), + last_image_compass = -1, + id_clock = player:hud_add(lookup_clock[math.abs(corner)]), + last_image_clock = -1, + state = corner, + } +end) + +minetest.register_chatcommand("compass", { + params = "[]", + description = "Change display of HUD Compass.", + privs = {}, + func = function(pname, params) + local player = minetest.get_player_by_name(pname) + if params and string.len(params) > 0 then -- includes corner parameter + local corner = tonumber(string.match(params, "^%d$")) + if corner and corner == 0 then -- disable compass and clock + player:hud_change(hud_compass[pname].id_compass, "text", "") -- blank hud compass + hud_compass[pname].last_image_compass = -1 + player:hud_change(hud_compass[pname].id_clock, "text", "") -- blank hud clock + hud_compass[pname].last_image_clock = -1 + hud_compass[pname].state = -1 * math.abs(hud_compass[pname].state) + storage:set_string(pname, hud_compass[pname].state) + elseif corner and corner > 0 and corner <= 4 then -- enable compass only to given corner + player:hud_remove(hud_compass[pname].id_compass) -- remove old hud compass + hud_compass[pname].last_image_compass = -1 + player:hud_remove(hud_compass[pname].id_clock) -- remove old hud clock + hud_compass[pname].last_image_clock = -1 + hud_compass[pname].id_compass = player:hud_add(lookup_compass[corner]) -- place new hud compass at requested corner + hud_compass[pname].state = corner + storage:set_string(pname, corner) + elseif corner and corner >= 5 and corner <= 8 then -- enable compass and clock to given corner + player:hud_remove(hud_compass[pname].id_compass) -- remove old hud compass + hud_compass[pname].last_image_compass = -1 + player:hud_remove(hud_compass[pname].id_clock) -- remove old hud clock + hud_compass[pname].last_image_clock = -1 + hud_compass[pname].id_compass = player:hud_add(lookup_compass[corner]) -- place new hud compass at requested corner + hud_compass[pname].id_clock = player:hud_add(lookup_clock[corner]) -- place new hud clock at requested corner + hud_compass[pname].state = corner + storage:set_string(pname, corner) + end + else -- just toggle hud + if hud_compass[pname].state > 0 then -- is enabled + player:hud_change(hud_compass[pname].id_compass, "text", "") -- blank hud compass + hud_compass[pname].last_image_compass = -1 + player:hud_change(hud_compass[pname].id_clock, "text", "") -- blank hud clock + hud_compass[pname].last_image_clock = -1 + end + hud_compass[pname].state = -1 * hud_compass[pname].state -- toggle state + storage:set_string(pname, hud_compass[pname].state) + end + end, +}) + +minetest.register_on_leaveplayer(function(player) + local pname = player:get_player_name() + if hud_compass[pname] then + hud_compass[pname] = nil + end +end) + +minetest.register_globalstep(function(dtime) + local players = minetest.get_connected_players() + for i,player in ipairs(players) do + local pname = player:get_player_name() + local dir = player:get_look_horizontal() + -- Calculate image indexes for compass and clock. + local angle_relative = math.deg(dir) + local image_compass = math.floor((angle_relative/22.5) + 0.5)%16 + local image_clock = math.floor(24 * minetest.get_timeofday()) + + if hud_compass[pname].state > 0 and image_compass ~= hud_compass[pname].last_image_compass then + local rc = player:hud_change(hud_compass[pname].id_compass, "text", "realcompass_"..image_compass..".png") + -- Check return code, seems to fix occasional startup glitch. + if rc == 1 then + hud_compass[pname].last_image_compass = image_compass + end + end + if hud_compass[pname].state >= 5 and image_clock ~= hud_compass[pname].last_image_clock then + local rc = player:hud_change(hud_compass[pname].id_clock, "text", "hud_24hr_clock_"..image_clock..".png") + -- Check return code, seems to fix occasional startup glitch. + if rc == 1 then + hud_compass[pname].last_image_clock = image_clock + end + end + end +end) diff --git a/mods/hud_compass/mod.conf b/mods/hud_compass/mod.conf new file mode 100644 index 00000000..1317ca8d --- /dev/null +++ b/mods/hud_compass/mod.conf @@ -0,0 +1,5 @@ +name = hud_compass +description = Optionally place a HUD compass and 24-hour clock on the screen. +release = 29223 +author = kestral +title = HUD Compass diff --git a/mods/hud_compass/screenshot.png b/mods/hud_compass/screenshot.png new file mode 100644 index 00000000..34f17605 Binary files /dev/null and b/mods/hud_compass/screenshot.png differ diff --git a/mods/hud_compass/settingtypes.txt b/mods/hud_compass/settingtypes.txt new file mode 100644 index 00000000..07ff95fd --- /dev/null +++ b/mods/hud_compass/settingtypes.txt @@ -0,0 +1,8 @@ +# This file contains settings for wand_of_illumination that can be changed in +# minetest.conf + +# Define default to place compass and whether to use clock. +# -1 to -4 = compass only (disabled), -5 to -8 = compass + clock (disabled) +# 1 to 4 = compass only (enabled), 5 to 8 = compass + clock (enabled) +# (default = -2) +compass_default_corner (Default corner to place compass) enum -2 -8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8 diff --git a/mods/hud_compass/textures/hud_24hr_clock_0.png b/mods/hud_compass/textures/hud_24hr_clock_0.png new file mode 100644 index 00000000..cffc8304 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_0.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_1.png b/mods/hud_compass/textures/hud_24hr_clock_1.png new file mode 100644 index 00000000..0187e08e Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_1.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_10.png b/mods/hud_compass/textures/hud_24hr_clock_10.png new file mode 100644 index 00000000..2ddbf8a9 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_10.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_11.png b/mods/hud_compass/textures/hud_24hr_clock_11.png new file mode 100644 index 00000000..2f2a212e Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_11.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_12.png b/mods/hud_compass/textures/hud_24hr_clock_12.png new file mode 100644 index 00000000..6ea67b2b Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_12.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_13.png b/mods/hud_compass/textures/hud_24hr_clock_13.png new file mode 100644 index 00000000..6611a78d Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_13.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_14.png b/mods/hud_compass/textures/hud_24hr_clock_14.png new file mode 100644 index 00000000..6a0a0373 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_14.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_15.png b/mods/hud_compass/textures/hud_24hr_clock_15.png new file mode 100644 index 00000000..5d8c4f24 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_15.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_16.png b/mods/hud_compass/textures/hud_24hr_clock_16.png new file mode 100644 index 00000000..38d981ab Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_16.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_17.png b/mods/hud_compass/textures/hud_24hr_clock_17.png new file mode 100644 index 00000000..0ac013e7 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_17.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_18.png b/mods/hud_compass/textures/hud_24hr_clock_18.png new file mode 100644 index 00000000..47d70a02 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_18.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_19.png b/mods/hud_compass/textures/hud_24hr_clock_19.png new file mode 100644 index 00000000..7fa91f4d Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_19.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_2.png b/mods/hud_compass/textures/hud_24hr_clock_2.png new file mode 100644 index 00000000..2a8dda4f Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_2.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_20.png b/mods/hud_compass/textures/hud_24hr_clock_20.png new file mode 100644 index 00000000..0739d167 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_20.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_21.png b/mods/hud_compass/textures/hud_24hr_clock_21.png new file mode 100644 index 00000000..53f3cff3 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_21.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_22.png b/mods/hud_compass/textures/hud_24hr_clock_22.png new file mode 100644 index 00000000..e8e32ad8 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_22.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_23.png b/mods/hud_compass/textures/hud_24hr_clock_23.png new file mode 100644 index 00000000..4158a77f Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_23.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_3.png b/mods/hud_compass/textures/hud_24hr_clock_3.png new file mode 100644 index 00000000..06761c8f Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_3.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_4.png b/mods/hud_compass/textures/hud_24hr_clock_4.png new file mode 100644 index 00000000..66b66e4a Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_4.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_5.png b/mods/hud_compass/textures/hud_24hr_clock_5.png new file mode 100644 index 00000000..41ec7bfa Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_5.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_6.png b/mods/hud_compass/textures/hud_24hr_clock_6.png new file mode 100644 index 00000000..c23f6ae6 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_6.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_7.png b/mods/hud_compass/textures/hud_24hr_clock_7.png new file mode 100644 index 00000000..782dadd5 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_7.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_8.png b/mods/hud_compass/textures/hud_24hr_clock_8.png new file mode 100644 index 00000000..e514ac1f Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_8.png differ diff --git a/mods/hud_compass/textures/hud_24hr_clock_9.png b/mods/hud_compass/textures/hud_24hr_clock_9.png new file mode 100644 index 00000000..6c800a39 Binary files /dev/null and b/mods/hud_compass/textures/hud_24hr_clock_9.png differ diff --git a/mods/hud_compass/textures/realcompass_0.png b/mods/hud_compass/textures/realcompass_0.png new file mode 100644 index 00000000..cff66cb9 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_0.png differ diff --git a/mods/hud_compass/textures/realcompass_1.png b/mods/hud_compass/textures/realcompass_1.png new file mode 100644 index 00000000..dfe027ea Binary files /dev/null and b/mods/hud_compass/textures/realcompass_1.png differ diff --git a/mods/hud_compass/textures/realcompass_10.png b/mods/hud_compass/textures/realcompass_10.png new file mode 100644 index 00000000..42285d43 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_10.png differ diff --git a/mods/hud_compass/textures/realcompass_11.png b/mods/hud_compass/textures/realcompass_11.png new file mode 100644 index 00000000..d5dba540 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_11.png differ diff --git a/mods/hud_compass/textures/realcompass_12.png b/mods/hud_compass/textures/realcompass_12.png new file mode 100644 index 00000000..ebb4bcd8 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_12.png differ diff --git a/mods/hud_compass/textures/realcompass_13.png b/mods/hud_compass/textures/realcompass_13.png new file mode 100644 index 00000000..b02c7700 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_13.png differ diff --git a/mods/hud_compass/textures/realcompass_14.png b/mods/hud_compass/textures/realcompass_14.png new file mode 100644 index 00000000..948dd1f3 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_14.png differ diff --git a/mods/hud_compass/textures/realcompass_15.png b/mods/hud_compass/textures/realcompass_15.png new file mode 100644 index 00000000..ac8005d0 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_15.png differ diff --git a/mods/hud_compass/textures/realcompass_2.png b/mods/hud_compass/textures/realcompass_2.png new file mode 100644 index 00000000..6d5af280 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_2.png differ diff --git a/mods/hud_compass/textures/realcompass_3.png b/mods/hud_compass/textures/realcompass_3.png new file mode 100644 index 00000000..59944dfb Binary files /dev/null and b/mods/hud_compass/textures/realcompass_3.png differ diff --git a/mods/hud_compass/textures/realcompass_4.png b/mods/hud_compass/textures/realcompass_4.png new file mode 100644 index 00000000..9a0dc9a1 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_4.png differ diff --git a/mods/hud_compass/textures/realcompass_5.png b/mods/hud_compass/textures/realcompass_5.png new file mode 100644 index 00000000..688b117e Binary files /dev/null and b/mods/hud_compass/textures/realcompass_5.png differ diff --git a/mods/hud_compass/textures/realcompass_6.png b/mods/hud_compass/textures/realcompass_6.png new file mode 100644 index 00000000..ebcf95f2 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_6.png differ diff --git a/mods/hud_compass/textures/realcompass_7.png b/mods/hud_compass/textures/realcompass_7.png new file mode 100644 index 00000000..c89a6a0f Binary files /dev/null and b/mods/hud_compass/textures/realcompass_7.png differ diff --git a/mods/hud_compass/textures/realcompass_8.png b/mods/hud_compass/textures/realcompass_8.png new file mode 100644 index 00000000..d9c3a595 Binary files /dev/null and b/mods/hud_compass/textures/realcompass_8.png differ diff --git a/mods/hud_compass/textures/realcompass_9.png b/mods/hud_compass/textures/realcompass_9.png new file mode 100644 index 00000000..caec9d7d Binary files /dev/null and b/mods/hud_compass/textures/realcompass_9.png differ diff --git a/mods/i_have_hands/LICENSE b/mods/i_have_hands/LICENSE new file mode 100644 index 00000000..7498974a --- /dev/null +++ b/mods/i_have_hands/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 KingTheGuy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mods/i_have_hands/README.md b/mods/i_have_hands/README.md new file mode 100644 index 00000000..429b479f --- /dev/null +++ b/mods/i_have_hands/README.md @@ -0,0 +1,68 @@ +![sample-gif](./repo-assets/crunchy.gif) + +--- + +Carry a storage block/node without breaking it. +you can carry a chest, a barrel.. just about anything that has an inventory. + +**how?** + +_Hold `sneak` then `right_click(place button)` with an `empty_hand` to pickup/place down the storage containers_ + +[view the roadmap](/ROADMAP.md) + +--- + +_1.0.9 CHANGELOG:_ + +bugfix: + +- crash when dropping a chest with pipeworks installed + +_1.0.8 CHANGELOG:_ + +bugfix: + +- VoxeLibre players could not open door with an empty hand + +_1.0.7 CHANGELOG:_ + +- \*\*add support for _mineclonia_ +- add a short delay to the hud popup +- don't show the hud when the player is already holding a "chest/inventory" + +_1.0.6 CHANGELOG:_ + +- updated gif +- hud over +- bug fixes + +_1.0.5 CHANGELOG:_ + +- added support for pipeworks [link](https://content.minetest.net/packages/mt-mods/pipeworks/) + - on storage containers pickup/drop tubes will update + +_1.0.4 CHANGELOG:_ + +- held storage containers can now be placed in water +- voxelibre chests once again render in hand + +_1.0.3 CHANGELOG:_ + +- added support for the drawers mod (Storage Drawers) [link](https://content.minetest.net/packages/LNJ/drawers/) +- faster animation + +- removed usage of keyword `goto` + +_1.0.2 CHANGELOG:_ + +- first person visual indicator +- block animation for placing down. +- added mcl double chest support +- can now view mcl chests in \*hand (not happy with how ive had to implement this) +- mcl disabled picking up shulkers +- bug fixes + +_1.0.1 CHANGELOG:_ + +- should take into account protected areas/blocks diff --git a/mods/i_have_hands/init.lua b/mods/i_have_hands/init.lua new file mode 100644 index 00000000..fe74007b --- /dev/null +++ b/mods/i_have_hands/init.lua @@ -0,0 +1,552 @@ +dofile(minetest.get_modpath("i_have_hands") .. "/utils.lua") + +--moving a hot furnace with just your hands.. i don't this so buddy +local RayDistance = 4; --this should be changed to the players reach +--invs to block +local blacklist = { "furnace", "shulker" } --if the name contains any of + +local data_storage = core.get_mod_storage() + +---@class Animate +---@field player table The damn player +---@field rotation integer Not sure if this is vector +---@field object table Is this a table? +---@field frame integer Not sure if frame is the correct term here +---@field item_name string This is probabliy the only thing that is correct +local to_animate = {} + +---@param this_string string the string +---@param split string sub to split at +function Split(this_string, split) + local new_word = {} + local index = string.find(this_string, split) + if index == nil then + return nil + end + local split_index = index + local split_start = "" + for x = 0, split_index - 1, 1 do + split_start = split_start .. string.sub(this_string, x, x) + end + new_word[1] = split_start + + local split_end = "" + for x = split_index + #split, #this_string, 1 do + split_end = split_end .. string.sub(this_string, x, x) + end + new_word[2] = split_end + return new_word +end + +local function placeDown(placer, rot, obj, above, frame, held_item_name) + table.insert(to_animate, + { player = placer, rot = rot, obj = obj, pos = above, frame = frame, item = held_item_name }) +end + +local function quantize_direction(yaw) + local angle = math.deg(yaw) % 360 -- Convert yaw to degrees and get its modulo 360 + if angle < 45 or angle >= 315 then + return math.rad(0) -- Facing North + elseif angle >= 45 and angle < 135 then + return math.rad(90) -- Facing East + elseif angle >= 135 and angle < 225 then + return math.rad(180) -- Facing South + else + return math.rad(270) -- Facing West + end +end + +--object, pos, frame +--not in use atm +local function animatePlace() + for i, v in pairs(to_animate) do + if v.frame == 0 then + v.obj:set_detach() + v.obj:set_yaw(v.rot) + local obj_rot = v.obj:get_rotation() + v.obj:set_rotation({ x = math.rad(-20), y = obj_rot.y, z = obj_rot.z }) + v.obj:set_properties({ visual_size = { x = 0.5, y = 0.5, z = 0.5 } }) + v.obj:set_pos(v.pos) + v.obj:set_properties({ pointable = true }) + core.sound_play({ name = "i_have_hands_pickup_node" }, { pos = v.pos, pitch = 0.7 }, true) + end + if v.frame == 1 then + local obj_rot = v.obj:get_rotation() + v.obj:set_rotation({ x = math.rad(0), y = obj_rot.y, z = obj_rot.z }) + v.obj:set_properties({ visual_size = { x = 0.6, y = 0.6, z = 0.6 } }) + end + if v.frame == 2 then + v.obj:set_properties({ visual_size = { x = 0.65, y = 0.65, z = 0.65 } }) + end + if v.frame == 1 then + local found_meta = data_storage:get_string(v.obj:get_luaentity().initial_pos) + data_storage:set_string(v.obj:get_luaentity().initial_pos, "") --clear it + core.set_node(v.pos, { name = v.item, param2 = core.dir_to_fourdir(core.yaw_to_dir(v.rot)) }) + core.sound_play({ name = "i_have_hands_place_down_node" }, { pos = v.pos }, true) + local meta = core.get_meta(v.pos) + + local node_containers = {} + for i, v in pairs(core.deserialize(found_meta)["data"]) do + local found_container = {} + for container, container_items in pairs(v) do + local found_inv = {} + if type(container_items) == "string" then + found_container[container] = container_items + else + for slot, item in pairs(container_items) do + found_inv[slot] = item + end + found_container[container] = found_inv + end + end + node_containers[i] = found_container + end + meta:from_table(node_containers) + + --NOTE(COMPAT): this adds support for the storage_drawers mod + if core.get_modpath("drawers") and drawers then + drawers.spawn_visuals(v.pos) + end + --NOTE(COMPAT): pipeworks update pipe, on place down + if core.get_modpath("pipeworks") and pipeworks then + pipeworks.after_place(v.pos) + end + end + v.frame = v.frame + 1 + if v.frame >= 6 then + v.obj:remove() + v.obj = nil + table.remove(to_animate, i) + end + end +end + +---@param pos table +---@param user table +---@return boolean +local function checkProtection(pos, user) + local protected = core.is_protected(pos, user:get_player_name()) + local owner = core.get_meta(pos):get_string("owner") + local player_name = user:get_player_name() + if owner ~= "" then + if owner ~= player_name then + core.chat_send_player(player_name, core.colorize("pink", "You are not the owner.")) + return true + end + end + if protected then + core.chat_send_player(player_name, core.colorize("pink", "This is protected")) + return true + end + return false +end + +local function isInventory(meta) + local count = 0 + for _ in pairs(meta:to_table()["inventory"]) do count = count + 1 end + if count < 1 then --inve have a value of 1 or greater + return false + end + return true +end + +local function isBlacklisted(pos) + for _, v in ipairs(blacklist) do + if utils.StringContains(core.get_node(pos).name, v) then + return true + end + end + return false +end + +local function find_empty_position(pos, radius) + local x, y, z = pos.x, pos.y, pos.z + local found = false + local empty_pos = nil + + for r = 0, radius do + for a = 0, 360, 10 do + local dx = math.floor(r * math.cos(math.rad(a))) + local dz = math.floor(r * math.sin(math.rad(a))) + local nx, nz = x + dx, z + dz + local ny = y + + while ny < 100 and not found do + local node = minetest.get_node({ x = nx, y = ny, z = nz }) + if node.name == "air" then + empty_pos = { x = nx, y = ny, z = nz } + found = true + end + ny = ny + 1 + end + end + end + + return empty_pos +end + +local handdef = core.registered_items[""] +local on_place = handdef and handdef.on_place + +local function hands(itemstack, placer, pointed_thing) + local contains = false + if placer:get_player_control()["sneak"] == true then + -- core.debug("what is this?",core.get_node(pointed_thing.under).name) + -- core.debug(string.format("location: %s", dump(core.get_modpath("drawers")))) + -- core.debug(core.colorize("yellow", "howdy mate, ive got the shits")) + if #placer:get_children() > 0 then --this is getting all connect objects + for index, obj in pairs(placer:get_children()) do + -- core.debug(dump(obj:get_luaentity().name)) + -- core.debug("got something: "..obj.name) + -- end + -- for index, value in pairs(placer:get_children()) do + local above = pointed_thing.above + -- core.debug("node: "..core.get_node(above).name) + if checkProtection(above, placer) == false then + if obj:get_luaentity().name == "i_have_hands:held" then + contains = true + -- core.debug("ok: "..type(held).."-"..held.."-") + local try_inside = core.registered_nodes[core.get_node(pointed_thing.under).name] + -- core.debug("buildabled? ",try_inside.buildable_to) + if core.get_node(above).name ~= "air" then + -- if core.get_node(above).name == "water" then + if utils.StringContains(core.get_node(above).name, "water") then + --do nothing + else + return itemstack + end + end + if #core.get_objects_inside_radius(above, 0.5) > 0 then + return itemstack + end + if try_inside.buildable_to == true then + above = pointed_thing.under + end + + + local held_item_name = core.registered_nodes[obj:get_properties().wield_item].name + -- local player_p = core.dir_to_fourdir(placer:get_look_dir()) + -- obj:set_pos(above) + -- animatePlace(obj,above) + + -- table.insert(to_animate, + -- { player = placer, rot = rot, obj = obj, pos = above, frame = 0, item = held_item_name }) + local rot = quantize_direction(placer:get_look_horizontal()) + placeDown(placer, rot, obj, above, 0, held_item_name) + end + end + end + end + + if contains == false then + local is_blacklisted = false + + if isBlacklisted(pointed_thing.under) then + is_blacklisted = true + end + if is_blacklisted == false then + if checkProtection(pointed_thing.under, placer) then + return itemstack + end + local meta = core.get_meta(pointed_thing.under) + if isInventory(meta) == false then + return itemstack + end + local obj = core.add_entity(placer:get_pos(), "i_have_hands:held") + obj:set_attach(placer, "", { x = 0, y = 9, z = 3.2 }, { x = 0, y = math.rad(90), z = 0 }, true) + --NOTE: attaching to the head just does not look very good, so lets not do that. + -- obj:set_attach(placer, "Head", { x = 0, y = -2, z = -3.2 }, { x = 0, y = math.rad(90), z = 0 }, true) + obj:set_properties({ + wield_item = core.registered_nodes[core.get_node(pointed_thing.under).name] + .name + }) + obj:get_luaentity().initial_pos = vector.to_string(obj:get_pos()) + + --NOTE(COMPAT): this takes care of voxelibre chests + if utils.StringContains(core.registered_nodes[core.get_node(pointed_thing.under).name].name, "mcl_chests") then + obj:set_properties({ wield_item = "mcl_chests:chest" }) + end + + -- core.debug(core.colorize("yellow",dump(core.registered_nodes[core.get_node(pointed_thing.under).name]))) + -- core.debug(core.colorize("blue", "all: \n" .. dump(meta:to_table()))) + local node_containers = {} + for i, v in pairs(meta:to_table()) do + local found_container = {} + for container, container_items in pairs(v) do + local found_inv = {} + if type(container_items) == "table" then + for slot, item in pairs(container_items) do + table.insert(found_inv, slot, item:to_string()) + end + found_container[container] = found_inv + else + found_container[container] = container_items + end + end + node_containers[i] = found_container + end + local full_data = { node = core.get_node(pointed_thing.under), data = node_containers } + -- core.debug("full_data: ".. dump(full_data.data)) + + local pos = vector.to_string(obj:get_pos()) + data_storage:set_string(pos, core.serialize(full_data)) + obj:get_luaentity().initial_pos = pos + -- placer:get_meta():set_string("obj_obj",core.write_json(obj)) + core.remove_node(pointed_thing.under) + core.sound_play({ name = "i_have_hands_pickup_node" }, { pos = pointed_thing.under }, true) + + --NOTE(COMPAT): pipeworks update pipe, on pickup + if core.get_modpath("pipeworks") and pipeworks then + pipeworks.after_place(pointed_thing.under) + end + + -- core.sound_play({ name = "i_have_hands_pickup" }, { pos = pointed_thing.under,gain = 0.1}, true) + end + end + end + --you know, return itemstack +end + +-- local original_on_place = minetest.registered_items[""].on_place + +core.override_item("", { + on_place = function(itemstack, placer, pointed_thing) + itemstack = on_place(itemstack, placer, pointed_thing) + hands(itemstack, placer, pointed_thing) + + -- Call the original on_place function if it exists + -- if original_on_place then + -- return original_on_place(itemstack, placer, pointed_thing) + -- end + return itemstack + end, + -- on_secondary_use = function(itemstack, placer, pointed_thing) + -- hands(itemstack, placer, pointed_thing) + -- end +}) + +--check if the player is holding an inventory +local function isHolding(player) + if #player:get_children() > 0 then --this is getting all connect objects + for index, obj in pairs(player:get_children()) do + if obj:get_luaentity().name == "i_have_hands:held" then + -- core.debug("this dude is holding") + return true + end + -- core.debug("nope not holding") + return false + end + end + return false +end + +core.register_entity("i_have_hands:held", { + selectionbox = { -0.0, -0.0, -0.0, 0.0, 0.0, 0.0, rotate = false }, + pointable = false, + physical = false, + collide_with_objects = false, + visual = "item", + wield_item = "", + visual_size = { x = 0.35, y = 0.35, z = 0.35 }, + _initial_pos = "", + on_step = function(self, dtime, moveresult) + -- core.debug(core.colorize("cyan", "dropping: \n" .. dump(data_storage:get_keys()))) + + if self.object:get_attach() == nil then + local contains = false + for i, v in pairs(to_animate) do + if v.obj == self.object then + -- core.debug("should not delete this yet") + contains = true + end + end + if contains == false then + local pos = self.object:get_luaentity().initial_pos + for i, v in pairs(data_storage:get_keys()) do + if v == pos then + core.set_node(vector.from_string(pos), core.deserialize(data_storage:get_string(v))["node"]) + local meta = core.get_meta(vector.from_string(pos)) + meta:from_table(utils.DeserializeMetaData(core.deserialize(data_storage:get_string(v))["data"])) + data_storage:set_string(v, "") + end + end + self.object:remove() + end + end + + --updute pos and data + if self.object:get_luaentity() then + if self.object:get_luaentity().initial_pos ~= nil then + local pos = self.object:get_luaentity().initial_pos + local data = data_storage:get_string(pos) + data_storage:set_string(pos) + self.object:get_luaentity().initial_pos = vector.to_string(self.object:get_pos()) + data_storage:set_string(vector.to_string(self.object:get_pos()), data) + end + end + end, +}) + +local player_hud_id = {} + +local function getPlayerFromPlayerHuds(player_name) + for _, ph in ipairs(player_hud_id) do + if ph.player_name == player_name then + return ph + end + end + return nil +end + +local function getPlayerHud(player_name) + -- core.debug("player_huds are " .. #player_hud_id .. " in length.") + for _, ph in ipairs(player_hud_id) do + if ph.player_name == player_name then + if ph.player_hud == nil then return nil end + return ph.player_hud + end + end +end + +local function removePlayerHud(player) + local hud_id = getPlayerHud(player:get_player_name()) + if hud_id ~= nil then + player:hud_remove(hud_id) + for index, ph in ipairs(player_hud_id) do + if ph.player_name == player:get_player_name() then + table.remove(player_hud_id, index) + end + end + end +end + +--FIXME: only raycast if the player's "hand" is empty (no need to cast when the player cant event pick it up to start with) +local function raycast() + local player = core.get_connected_players() + if #player > 0 then + for _, p in ipairs(player) do + local eye_height = p:get_properties().eye_height + local player_look_dir = p:get_look_dir() + local pos = p:get_pos():add(player_look_dir) + local player_pos = { x = pos.x, y = pos.y + eye_height, z = pos.z } + local new_pos = p:get_look_dir():multiply(RayDistance):add(player_pos) + local raycast_result = core.raycast(player_pos, new_pos, false, false):next() + if isHolding(p) then + removePlayerHud(p) + return + end + + local hud_id = nil; --FIXME this need to be added to list of all player HUDS + if raycast_result then + local pointed_node = core.get_node(raycast_result.under) + if isBlacklisted(raycast_result.under) then + removePlayerHud(p) + return + end + if p:get_wielded_item():get_name() ~= "" then + removePlayerHud(p) + return + end + if isInventory(core.get_meta(raycast_result.under)) then + hud_id = getPlayerHud(p:get_player_name()) + local player_with_hud = getPlayerFromPlayerHuds(p:get_player_name()) + if player_with_hud == nil then + local this_players_hud = { player_name = p:get_player_name(), player_hud = hud_id , hud_delay = 6, chest_location = raycast_result.under} + table.insert(player_hud_id, this_players_hud) + else + -- core.debug("what do we have here? "..player_with_hud.hud_delay) + if player_with_hud.hud_delay == 0 then + if hud_id == nil then + hud_id = p:hud_add({ + hud_elem_type = "text", + position = { x = 0.5, y = 0.6 }, + direction = 0, + name = "ihh", + scale = { x = 1, y = 1 }, + -- text = "crouch & interact to lift this", + text = "Carry: crouch & interact", + number = "0xFFFFFF", + z_index = 0, + }) + end + player_with_hud.player_hud = hud_id + end + if player_with_hud.chest_location ~= raycast_result.under then + removePlayerHud(p) + end + end + -- core.debug("so wtf is this then? " .. tostring(hud_id)) + else + removePlayerHud(p) + end + -- core.debug(string.format("what is this?",core.registered_nodes[pointed_node].name)) + else + removePlayerHud(p) + end + end + end +end + +local function hotbarSlotNotEmpty() + local player = core.get_connected_players() + if #player > 0 then + for _, p in ipairs(player) do + if p:get_wielded_item():get_name() ~= "" then + if #p:get_children() > 0 then --this is getting all connect objects + for index, obj in pairs(p:get_children()) do + if obj:get_luaentity().name == "i_have_hands:held" then + local held_item_name = core.registered_nodes[obj:get_properties().wield_item].name + placeDown(p, 0, obj, find_empty_position(p:get_pos(), 10), 0, held_item_name) + end + end + end + end + end + end +end + +local function tickHudDelay() + for _,h in pairs(player_hud_id) do + if h.hud_delay > 0 then + h.hud_delay = h.hud_delay - 1 + end + end +end + + +local ran_once = false +local tick = 0 +core.register_globalstep(function(dtime) + -- raycast() + tick = tick + 0.5 + if tick > 2 then + animatePlace() + raycast() + hotbarSlotNotEmpty() + tickHudDelay() + tick = 0 + end + if ran_once == false then + ran_once = true + for i, v in pairs(data_storage:get_keys()) do + local pos = vector.from_string(v) + core.set_node(pos, core.deserialize(data_storage:get_string(v))["node"]) + local meta = core.get_meta(pos) + meta:from_table(utils.DeserializeMetaData(core.deserialize(data_storage:get_string(v))["data"])) + data_storage:set_string(v, "") + end + end +end) + + +core.register_on_dieplayer(function(ObjectRef, reason) + if #ObjectRef:get_children() > 0 then --this is getting all connect objects + for index, obj in pairs(ObjectRef:get_children()) do + if obj:get_luaentity().name == "i_have_hands:held" then + local held_item_name = core.registered_nodes[obj:get_properties().wield_item].name + placeDown(ObjectRef, 0, obj, find_empty_position(ObjectRef:get_pos(), 10), 0, held_item_name) + end + end + end + -- core.debug("what death? " .. ObjectRef:get_player_name()) +end) diff --git a/mods/i_have_hands/mod.conf b/mods/i_have_hands/mod.conf new file mode 100644 index 00000000..9818ac0e --- /dev/null +++ b/mods/i_have_hands/mod.conf @@ -0,0 +1,6 @@ +name = i_have_hands +description = Carry nodes/blocks with inventories without breaking them. +author = SURV +title = I Have Hands +optional_depends = drawers, pipeworks, mcl_meshhand +release = 30818 diff --git a/mods/i_have_hands/sounds/i_have_hands_pickup_node.ogg b/mods/i_have_hands/sounds/i_have_hands_pickup_node.ogg new file mode 100644 index 00000000..17a6b30b Binary files /dev/null and b/mods/i_have_hands/sounds/i_have_hands_pickup_node.ogg differ diff --git a/mods/i_have_hands/sounds/i_have_hands_place_down_node.ogg b/mods/i_have_hands/sounds/i_have_hands_place_down_node.ogg new file mode 100644 index 00000000..83a6a407 Binary files /dev/null and b/mods/i_have_hands/sounds/i_have_hands_place_down_node.ogg differ diff --git a/mods/i_have_hands/utils.lua b/mods/i_have_hands/utils.lua new file mode 100644 index 00000000..681a3b13 --- /dev/null +++ b/mods/i_have_hands/utils.lua @@ -0,0 +1,56 @@ +utils = {} + +function utils.Distance(x1, y1, z1, x2, y2, z2) + local dx = x2 - x1 + local dy = y2 - y1 + local dz = z2 - z1 + return math.sqrt(dx * dx + dy * dy + dz * dz) +end + +function utils.StringContains(str, find) + str = string.upper(str) + find = string.upper(find) + local i, _ = string.find(str, find) + return i +end + +function utils.SerializeMetaData(data) + local node_containers = {} + for i, v in pairs(data:to_table()) do + local found_container = {} + for container, container_items in pairs(v) do + local found_inv = {} + if type(container_items) == "table" then + for slot, item in pairs(container_items) do + table.insert(found_inv, slot, item:to_string()) + end + found_container[container] = found_inv + else + found_container[container] = container_items + end + end + node_containers[i] = found_container + end + return core.serialize(node_containers) +end + +function utils.DeserializeMetaData(data) + local node_containers = {} + for i, v in pairs(data) do + local found_container = {} + for container, container_items in pairs(v) do + local found_inv = {} + if type(container_items) == "string" then + found_container[container] = container_items + else + for slot, item in pairs(container_items) do + found_inv[slot] = item + end + found_container[container] = found_inv + end + end + node_containers[i] = found_container + end + return node_containers +end + diff --git a/mods/more_boats/LICENSE b/mods/more_boats/LICENSE new file mode 100644 index 00000000..0cbc674b --- /dev/null +++ b/mods/more_boats/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 SkyBuilder1717 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/mods/more_boats/README.md b/mods/more_boats/README.md new file mode 100644 index 00000000..7a379a87 --- /dev/null +++ b/mods/more_boats/README.md @@ -0,0 +1,6 @@ +# more-boats +More Boats — Minetest mod, which adds more boat variants based on original Minetest Game materials and other stuff. + +[Authors profile](https://content.minetest.net/users/SkyBuilder1717/) +[Mod on ContentDB](https://content.minetest.net/packages/SkyBuilder1717/more_boats/) +[Mod on MultiCraft](https://content.multicraft.world/packages/SkyBuilder1717/more_boats/) diff --git a/mods/more_boats/api.lua b/mods/more_boats/api.lua new file mode 100644 index 00000000..0c1ca2d0 --- /dev/null +++ b/mods/more_boats/api.lua @@ -0,0 +1,276 @@ +more_boat = { + LAVA = {} +} +local S = minetest.get_translator("boats") +local function is_lava(pos) + local nn = minetest.get_node(pos).name + return minetest.get_item_group(nn, "lava") ~= 0 +end +local function is_water(pos) + local nn = minetest.get_node(pos).name + return minetest.get_item_group(nn, "water") ~= 0 +end +local function get_velocity(v, yaw, y) + local x = -math.sin(yaw) * v + local z = math.cos(yaw) * v + return {x = x, y = y, z = z} +end +local function get_v(v) + return math.sqrt(v.x ^ 2 + v.z ^ 2) +end +function more_boat.on_rightclick(self, clicker) + if not clicker or not clicker:is_player() then + return + end + local name = clicker:get_player_name() + if self.driver and name == self.driver then + clicker:set_detach() + + player_api.set_animation(clicker, "stand", 30) + local pos = clicker:get_pos() + pos = {x = pos.x, y = pos.y + 0.2, z = pos.z} + minetest.after(0.1, function() + clicker:set_pos(pos) + end) + elseif not self.driver then + clicker:set_attach(self.object, "", + {x = 0.5, y = 1, z = -3}, {x = 0, y = 0, z = 0}) + + self.driver = name + player_api.player_attached[name] = true + + minetest.after(0.2, function() + player_api.set_animation(clicker, "sit", 30) + end) + clicker:set_look_horizontal(self.object:get_yaw()) + end +end +function more_boat.on_detach_child(self, child) + if child and child:get_player_name() == self.driver then + player_api.player_attached[child:get_player_name()] = false + + self.driver = nil + self.auto = false + end +end +function more_boat.on_activate(self, staticdata, dtime_s) + self.object:set_armor_groups({immortal = 1}) + if staticdata then + self.v = tonumber(staticdata) + end + self.last_v = self.v +end +function more_boat.get_staticdata(self) + return tostring(self.v) +end +function more_boat.on_punch(self, puncher) + if not puncher or not puncher:is_player() or self.removed then + return + end + local name = puncher:get_player_name() + if self.driver and name == self.driver then + self.driver = nil + puncher:set_detach() + player_api.player_attached[name] = false + end + if not self.driver then + self.removed = true + local inv = puncher:get_inventory() + local luamob = self.object:get_luaentity().name + if not minetest.is_creative_enabled(name) --then + or not inv:contains_item("main", luamob) then + local leftover = inv:add_item("main", luamob) + if not leftover:is_empty() then + minetest.add_item(self.object:get_pos(), leftover) + end + end + local name = puncher:get_player_name() + minetest.after(0.1, function() + self.object:remove() + end) + end +end +function more_boat.on_step(self, dtime) + self.v = get_v(self.object:get_velocity()) * math.sign(self.v) + if self.driver then + local driver_objref = minetest.get_player_by_name(self.driver) + if driver_objref then + local ctrl = driver_objref:get_player_control() + if ctrl.up and ctrl.down then + if not self.auto then + self.auto = true + minetest.chat_send_player(self.driver, S("Boat cruise mode on")) + end + elseif ctrl.down then + self.v = self.v - dtime * 2.0 + if self.auto then + self.auto = false + minetest.chat_send_player(self.driver, S("Boat cruise mode off")) + end + elseif ctrl.up or self.auto then + self.v = self.v + dtime * 2.0 + end + if ctrl.left then + if self.v < -0.001 then + self.object:set_yaw(self.object:get_yaw() - dtime * 0.9) + else + self.object:set_yaw(self.object:get_yaw() + dtime * 0.9) + end + elseif ctrl.right then + if self.v < -0.001 then + self.object:set_yaw(self.object:get_yaw() + dtime * 0.9) + else + self.object:set_yaw(self.object:get_yaw() - dtime * 0.9) + end + end + end + end + local velo = self.object:get_velocity() + if not self.driver and + self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then + self.object:set_pos(self.object:get_pos()) + return + end + local drag = dtime * math.sign(self.v) * (0.01 + 0.0796 * self.v * self.v) + if math.abs(self.v) <= math.abs(drag) then + self.v = 0 + else + self.v = self.v - drag + end + local p = self.object:get_pos() + p.y = p.y - 0.5 + local new_velo + local new_acce = {x = 0, y = 0, z = 0} + if not is_water(p) then + local nodedef = minetest.registered_nodes[minetest.get_node(p).name] + if (not nodedef) or nodedef.walkable then + self.v = 0 + new_acce = {x = 0, y = 1, z = 0} + else + new_acce = {x = 0, y = -9.8, z = 0} + end + new_velo = get_velocity(self.v, self.object:get_yaw(), + self.object:get_velocity().y) + self.object:set_pos(self.object:get_pos()) + else + p.y = p.y + 1 + if is_water(p) then + local y = self.object:get_velocity().y + if y >= 5 then + y = 5 + elseif y < 0 then + new_acce = {x = 0, y = 20, z = 0} + else + new_acce = {x = 0, y = 5, z = 0} + end + new_velo = get_velocity(self.v, self.object:get_yaw(), y) + self.object:set_pos(self.object:get_pos()) + else + new_acce = {x = 0, y = 0, z = 0} + if math.abs(self.object:get_velocity().y) < 1 then + local pos = self.object:get_pos() + pos.y = math.floor(pos.y) + 0.5 + self.object:set_pos(pos) + new_velo = get_velocity(self.v, self.object:get_yaw(), 0) + else + new_velo = get_velocity(self.v, self.object:get_yaw(), + self.object:get_velocity().y) + self.object:set_pos(self.object:get_pos()) + end + end + end + self.object:set_velocity(new_velo) + self.object:set_acceleration(new_acce) +end +function more_boat.LAVA.on_step(self, dtime) + self.v = get_v(self.object:get_velocity()) * math.sign(self.v) + if self.driver then + local driver_objref = minetest.get_player_by_name(self.driver) + if driver_objref then + local ctrl = driver_objref:get_player_control() + if ctrl.up and ctrl.down then + if not self.auto then + self.auto = true + minetest.chat_send_player(self.driver, S("Boat cruise mode on")) + end + elseif ctrl.down then + self.v = self.v - dtime * 2.0 + if self.auto then + self.auto = false + minetest.chat_send_player(self.driver, S("Boat cruise mode off")) + end + elseif ctrl.up or self.auto then + self.v = self.v + dtime * 2.0 + end + if ctrl.left then + if self.v < -0.001 then + self.object:set_yaw(self.object:get_yaw() - dtime * 0.9) + else + self.object:set_yaw(self.object:get_yaw() + dtime * 0.9) + end + elseif ctrl.right then + if self.v < -0.001 then + self.object:set_yaw(self.object:get_yaw() + dtime * 0.9) + else + self.object:set_yaw(self.object:get_yaw() - dtime * 0.9) + end + end + end + end + local velo = self.object:get_velocity() + if not self.driver and + self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then + self.object:set_pos(self.object:get_pos()) + return + end + local drag = dtime * math.sign(self.v) * (0.01 + 0.0796 * self.v * self.v) + if math.abs(self.v) <= math.abs(drag) then + self.v = 0 + else + self.v = self.v - drag + end + local p = self.object:get_pos() + p.y = p.y - 0.5 + local new_velo + local new_acce = {x = 0, y = 0, z = 0} + if not is_lava(p) then + local nodedef = minetest.registered_nodes[minetest.get_node(p).name] + if (not nodedef) or nodedef.walkable then + self.v = 0 + new_acce = {x = 0, y = 1, z = 0} + else + new_acce = {x = 0, y = -9.8, z = 0} + end + new_velo = get_velocity(self.v, self.object:get_yaw(), + self.object:get_velocity().y) + self.object:set_pos(self.object:get_pos()) + else + p.y = p.y + 1 + if is_lava(p) then + local y = self.object:get_velocity().y + if y >= 5 then + y = 5 + elseif y < 0 then + new_acce = {x = 0, y = 20, z = 0} + else + new_acce = {x = 0, y = 5, z = 0} + end + new_velo = get_velocity(self.v, self.object:get_yaw(), y) + self.object:set_pos(self.object:get_pos()) + else + new_acce = {x = 0, y = 0, z = 0} + if math.abs(self.object:get_velocity().y) < 1 then + local pos = self.object:get_pos() + pos.y = math.floor(pos.y) + 0.5 + self.object:set_pos(pos) + new_velo = get_velocity(self.v, self.object:get_yaw(), 0) + else + new_velo = get_velocity(self.v, self.object:get_yaw(), + self.object:get_velocity().y) + self.object:set_pos(self.object:get_pos()) + end + end + end + self.object:set_velocity(new_velo) + self.object:set_acceleration(new_acce) +end diff --git a/mods/more_boats/init.lua b/mods/more_boats/init.lua new file mode 100644 index 00000000..3277b184 --- /dev/null +++ b/mods/more_boats/init.lua @@ -0,0 +1,164 @@ + +more_boats = {} +more_boats.boats = { + { + "Aspen Boat", + "more_boats:aspen_boat", + "more_boats_aspen_inv.png", + "more_boats_aspen_wield.png", + {"default_aspen_wood.png"}, + "default:aspen_wood" + }, + { + "Acacia Boat", + "more_boats:acacia_boat", + "more_boats_acacia_inv.png", + "more_boats_acacia_wield.png", + {"default_acacia_wood.png"}, + "default:acacia_wood" + }, + { + "Pine Boat", + "more_boats:pine_boat", + "more_boats_pine_inv.png", + "more_boats_pine_wield.png", + {"default_pine_wood.png"}, + "default:pine_wood" + }, + { + "Jungle Wood Boat", + "more_boats:jungle_boat", + "more_boats_jungle_inv.png", + "more_boats_jungle_wield.png", + {"default_junglewood.png"}, + "default:junglewood" + }, + { + "Obsidian Boat", + "more_boats:obsidian_boat", + "more_boats_obsidian_inv.png", + "more_boats_obsidian_wield.png", + {"default_obsidian.png"}, + "default:obsidian", + true + } +} +local S = minetest.get_translator("more_boats") +local modpath = minetest.get_modpath("more_boats") +dofile(modpath .."/api.lua") +minetest.clear_craft({ + output = "boats:boat", + recipe = { + {"", "", "" }, + {"group:wood", "", "group:wood"}, + {"group:wood", "group:wood", "group:wood"}, + }, +}) +minetest.register_craft({ + output = "boats:boat", + recipe = { + {"", "", ""}, + {"default:wood", "", "default:wood"}, + {"default:wood", "default:wood", "default:wood"}, + }, +}) +local function is_water(pos) + local nn = minetest.get_node(pos).name + return minetest.get_item_group(nn, "water") ~= 0 +end +local function is_lava(pos) + local nn = minetest.get_node(pos).name + return minetest.get_item_group(nn, "lava") ~= 0 +end +for i, def in ipairs(more_boats.boats) do + local desc = def[1] + local name = def[2] + local texture_inv = def[3] + local texture_wield = def[4] + local texture_boat = def[5] + local material = def[6] + local isnt_lava = def[7] + local boat_def = { + initial_properties = { + physical = true, + collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5}, + visual = "mesh", + mesh = "boats_boat.obj", + textures = texture_boat, + }, + driver = nil, + v = 0, + last_v = 0, + removed = false, + auto = false + } + boat_def.on_rightclick = more_boat.on_rightclick + boat_def.on_detach_child = more_boat.on_detach_child + boat_def.on_activate = more_boat.on_step + boat_def.get_staticdata = more_boat.get_staticdata + boat_def.on_punch = more_boat.on_punch + if not isnt_lava then + boat_def.on_step = more_boat.on_step + else + boat_def.on_step = more_boat.LAVA.on_step + end + minetest.register_entity(name, boat_def) + minetest.register_craftitem(name, { + description = S(desc), + inventory_image = texture_inv, + wield_image = texture_wield, + wield_scale = {x = 2, y = 2, z = 1}, + liquids_pointable = true, + groups = {flammable = 2}, + on_place = function(itemstack, placer, pointed_thing) + local under = pointed_thing.under + local node = minetest.get_node(under) + local udef = minetest.registered_nodes[node.name] + if udef and udef.on_rightclick and + not (placer and placer:is_player() and + placer:get_player_control().sneak) then + return udef.on_rightclick(under, node, placer, itemstack, + pointed_thing) or itemstack + end + if pointed_thing.type ~= "node" then + return itemstack + end + if not isnt_lava then + if not is_water(pointed_thing.under) then + return itemstack + end + else + if not is_lava(pointed_thing.under) then + return itemstack + end + end + pointed_thing.under.y = pointed_thing.under.y + 0.5 + boat = minetest.add_entity(pointed_thing.under, name) + if boat then + if placer then + boat:set_yaw(placer:get_look_horizontal()) + end + local player_name = placer and placer:get_player_name() or "" + if not minetest.is_creative_enabled(player_name) then + itemstack:take_item() + end + end + return itemstack + end, + }) + minetest.register_craft({ + output = name, + recipe = { + {"", "", ""}, + {material, "", material}, + {material, material, material}, + } + }) + if not is_lava then + minetest.register_craft({ + type = "fuel", + recipe = name, + burntime = 20, + }) + end +end diff --git a/mods/more_boats/locale/more_boats.de.tr b/mods/more_boats/locale/more_boats.de.tr new file mode 100644 index 00000000..17e5c946 --- /dev/null +++ b/mods/more_boats/locale/more_boats.de.tr @@ -0,0 +1,6 @@ +# textdomain: more_boats +Aspen Boat=Aspen Boot +Acacia Boat=Akazienboot +Jungle Wood Boat=Dschungelholzboot +Pine Boat=Kiefernboot +Obsidian Boat=Obsidian-Boot diff --git a/mods/more_boats/locale/more_boats.es.tr b/mods/more_boats/locale/more_boats.es.tr new file mode 100644 index 00000000..c6b2198b --- /dev/null +++ b/mods/more_boats/locale/more_boats.es.tr @@ -0,0 +1,6 @@ +# textdomain: more_boats +Aspen Boat=Bote de Aspen +Acacia Boat=Barco de Acacia +Jungle Wood Boat=Bote de Madera de Jungla +Pine Boat=Barco de Pino +Obsidian Boat=Barco de Obsidiana diff --git a/mods/more_boats/locale/more_boats.ru.tr b/mods/more_boats/locale/more_boats.ru.tr new file mode 100644 index 00000000..1431695e --- /dev/null +++ b/mods/more_boats/locale/more_boats.ru.tr @@ -0,0 +1,6 @@ +# textdomain: more_boats +Aspen Boat=Лодка из осины +Acacia Boat=Лодка из Акации +Jungle Wood Boat=Лодка из дерева джунглей +Pine Boat=Сосновая лодка +Obsidian Boat=Обсидиановая лодка diff --git a/mods/more_boats/locale/more_boats.uk.tr b/mods/more_boats/locale/more_boats.uk.tr new file mode 100644 index 00000000..cb1501ba --- /dev/null +++ b/mods/more_boats/locale/more_boats.uk.tr @@ -0,0 +1,6 @@ +# textdomain: more_boats +Aspen Boat=Човен з осики +Acacia Boat=Човен з акації +Jungle Wood Boat=Човен з дерева джунглів +Pine Boat=Сосновий човен +Obsidian Boat=Обсидіановий човен diff --git a/mods/more_boats/mod.conf b/mods/more_boats/mod.conf new file mode 100644 index 00000000..ccda292a --- /dev/null +++ b/mods/more_boats/mod.conf @@ -0,0 +1,7 @@ +name = more_boats +description = Adds more boat variants! +depends = default, boats +author = SkyBuilder1717 +title = More Boats + +release = 26081 diff --git a/mods/more_boats/settingtypes.txt b/mods/more_boats/settingtypes.txt new file mode 100644 index 00000000..c558fe44 --- /dev/null +++ b/mods/more_boats/settingtypes.txt @@ -0,0 +1,2 @@ +# Adds an additional protected boats for the game +more_boats_protected (Protected Boats) bool false \ No newline at end of file diff --git a/mods/more_boats/textures/README.TXT b/mods/more_boats/textures/README.TXT new file mode 100644 index 00000000..427d509f --- /dev/null +++ b/mods/more_boats/textures/README.TXT @@ -0,0 +1,10 @@ +-Boat creators- +Originally by PilzAdam (MIT) +Various Minetest developers and contributors (MIT) +Textures: Zeg9 (CC BY-SA 3.0) +Model: thetoon and Zeg9 (CC BY-SA 3.0) +modified by PavelS(SokolovPavel) (CC BY-SA 3.0) +modified by sofar (CC BY-SA 3.0) +-- + +Changed textures: SkyBuilder1717 (MIT) diff --git a/mods/more_boats/textures/more_boats_acacia_inv.png b/mods/more_boats/textures/more_boats_acacia_inv.png new file mode 100644 index 00000000..292bcba7 Binary files /dev/null and b/mods/more_boats/textures/more_boats_acacia_inv.png differ diff --git a/mods/more_boats/textures/more_boats_acacia_wield.png b/mods/more_boats/textures/more_boats_acacia_wield.png new file mode 100644 index 00000000..dd9d6976 Binary files /dev/null and b/mods/more_boats/textures/more_boats_acacia_wield.png differ diff --git a/mods/more_boats/textures/more_boats_aspen_inv.png b/mods/more_boats/textures/more_boats_aspen_inv.png new file mode 100644 index 00000000..e0810e6c Binary files /dev/null and b/mods/more_boats/textures/more_boats_aspen_inv.png differ diff --git a/mods/more_boats/textures/more_boats_aspen_wield.png b/mods/more_boats/textures/more_boats_aspen_wield.png new file mode 100644 index 00000000..da5a9aff Binary files /dev/null and b/mods/more_boats/textures/more_boats_aspen_wield.png differ diff --git a/mods/more_boats/textures/more_boats_jungle_inv.png b/mods/more_boats/textures/more_boats_jungle_inv.png new file mode 100644 index 00000000..6f5d154e Binary files /dev/null and b/mods/more_boats/textures/more_boats_jungle_inv.png differ diff --git a/mods/more_boats/textures/more_boats_jungle_wield.png b/mods/more_boats/textures/more_boats_jungle_wield.png new file mode 100644 index 00000000..331e9ae3 Binary files /dev/null and b/mods/more_boats/textures/more_boats_jungle_wield.png differ diff --git a/mods/more_boats/textures/more_boats_obsidian_inv.png b/mods/more_boats/textures/more_boats_obsidian_inv.png new file mode 100644 index 00000000..b718b814 Binary files /dev/null and b/mods/more_boats/textures/more_boats_obsidian_inv.png differ diff --git a/mods/more_boats/textures/more_boats_obsidian_wield.png b/mods/more_boats/textures/more_boats_obsidian_wield.png new file mode 100644 index 00000000..8000bcea Binary files /dev/null and b/mods/more_boats/textures/more_boats_obsidian_wield.png differ diff --git a/mods/more_boats/textures/more_boats_pine_inv.png b/mods/more_boats/textures/more_boats_pine_inv.png new file mode 100644 index 00000000..f9dd64f9 Binary files /dev/null and b/mods/more_boats/textures/more_boats_pine_inv.png differ diff --git a/mods/more_boats/textures/more_boats_pine_wield.png b/mods/more_boats/textures/more_boats_pine_wield.png new file mode 100644 index 00000000..194569c2 Binary files /dev/null and b/mods/more_boats/textures/more_boats_pine_wield.png differ diff --git a/mods/more_structures/LICENSE.txt b/mods/more_structures/LICENSE.txt new file mode 100644 index 00000000..083ff47e --- /dev/null +++ b/mods/more_structures/LICENSE.txt @@ -0,0 +1,27 @@ +MIT License + +Copyright (c) 2023 ClothierEdward + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Textures added into the mod follow the CC-BY-SA-4.0 license. +Click here to learn more about the CC-BY-SA-4.0 license. + +https://creativecommons.org/licenses/by-sa/4.0/ +https://creativecommons.org/licenses/by-sa/4.0/legalcode \ No newline at end of file diff --git a/mods/more_structures/README.txt b/mods/more_structures/README.txt new file mode 100644 index 00000000..787d0456 --- /dev/null +++ b/mods/more_structures/README.txt @@ -0,0 +1,11 @@ +The More Structures Mod (aka more_structures) + +This mod adds several new structures to make your Minetest world more alive. +============================================ + +License: MIT License (for code), CC-BY-SA-4.0 (for textures) + +Notes: +This mod was made by ClothierEdward (aka u/ClothierCrafter on Reddit) + +The mod icon is a screenshot which uses textures from Minetest Game. \ No newline at end of file diff --git a/mods/more_structures/depends.txt b/mods/more_structures/depends.txt new file mode 100644 index 00000000..f0584dc1 --- /dev/null +++ b/mods/more_structures/depends.txt @@ -0,0 +1,8 @@ +default +xpanes +vessels +doors +beds +stairs +lootchests +lootchests_default \ No newline at end of file diff --git a/mods/more_structures/description.txt b/mods/more_structures/description.txt new file mode 100644 index 00000000..18926fc7 --- /dev/null +++ b/mods/more_structures/description.txt @@ -0,0 +1 @@ +This mod adds several new structures to make your Minetest world more alive. \ No newline at end of file diff --git a/mods/more_structures/init.lua b/mods/more_structures/init.lua new file mode 100644 index 00000000..42178dc3 --- /dev/null +++ b/mods/more_structures/init.lua @@ -0,0 +1,606 @@ +morestructures = {} + +--Towers + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_grass", "default:dirt_with_snow", "default:dirt_with_coniferous_litter"}, + biomes = {"grassland", "deciduous_forest", "coniferous_forest", "taiga", "snowy_grassland"}, + sidelen = 100, + fill_ratio = 0.0000052, + y_max = 300, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/tower_1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Wells + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_grass", "default:dirt_with_snow", "default:dirt_with_coniferous_litter"}, + biomes = {"grassland"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 50, + y_min = 3, + schematic = minetest.get_modpath("more_structures") .. "/schematics/well_1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -7, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_grass", "default:dirt_with_snow", "default:dirt_with_coniferous_litter"}, + biomes = {"snowy_grassland"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 50, + y_min = 3, + schematic = minetest.get_modpath("more_structures") .. "/schematics/well_2.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -7, + rotation = "random", +}) + +--Desert Wells + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.000007, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/well_3.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.000007, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/well_4.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Campsites +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_grass"}, + biomes = {"grassland", "deciduous_forest"}, + sidelen = 100, + fill_ratio = 0.00001, + y_max = 300, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/campsite_1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -2, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_coniferous_litter"}, + biomes = {"coniferous_forest"}, + sidelen = 100, + fill_ratio = 0.00001, + y_max = 300, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/campsite_2.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -2, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_rainforest_litter"}, + biomes = {"rainforest"}, + sidelen = 100, + fill_ratio = 0.00001, + y_max = 300, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/campsite_3.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -2, + rotation = "random", +}) + +--Igloos +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_snow", "default:snowblock"}, + biomes = {"taiga", "snowy_grassland", "icesheet"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 50, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/igloo_1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_snow", "default:snowblock"}, + biomes = {"taiga", "snowy_grassland", "icesheet"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 50, + y_min = 7, + place_offset_y = -17, + schematic = minetest.get_modpath("more_structures") .. "/schematics/igloo_2.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_snow", "default:snowblock"}, + biomes = {"taiga", "snowy_grassland", "icesheet"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 50, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/igloo_3.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -17, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_snow", "default:snowblock"}, + biomes = {"taiga", "snowy_grassland", "icesheet"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 50, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/igloo_4.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -8, + rotation = "random", +}) + +--Sand Pillars +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00003, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/sand_pillar1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00004, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/sand_pillar2.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Desert Sand Pillars +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00005, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desertsand_pillar1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00003, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desertsand_pillar2.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Silver Sand Pillars +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:silver_sand"}, + biomes = {"cold_desert"}, + sidelen = 50, + fill_ratio = 0.00005, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/silversand_pillar1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:silver_sand"}, + biomes = {"cold_desert"}, + sidelen = 50, + fill_ratio = 0.00003, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/silversand_pillar2.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Obsidian Pillars +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand", "default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.0000008, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/obsidian_pillar1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand", "default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.0000005, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/obsidian_pillar2.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Sand Arches +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00004, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/sand_arch1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/sand_arch2.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Desert Sand Arches +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00005, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desertsand_arch1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.00002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desertsand_arch2.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Silver Sand Arches +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:silver_sand"}, + biomes = {"cold_desert"}, + sidelen = 50, + fill_ratio = 0.00005, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/silversand_arch1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:silver_sand"}, + biomes = {"cold_desert"}, + sidelen = 50, + fill_ratio = 0.00002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/silversand_arch2.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Mese Hoards +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone"}, + sidelen = 100, + fill_ratio = 0.000004, + y_max = -1024, + y_min = -31000, + schematic = minetest.get_modpath("more_structures") .. "/schematics/mese_hoard.mts", + flags = "place_center_x, place_center_z, force_placement, all_floors", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "simple", + place_on = {"default:grass"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = 300, + y_min = 7, + decoration = "more_structures:villagehouse_1", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Jungle Temple +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:dirt_with_rainforest_litter", "default:dirt"}, + sidelen = 100, + fill_ratio = 0.00002, + y_max = 100, + y_min = 5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/jungle_temple.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Stonehenge +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:dirt", "default:dirt_with_grass"}, + biomes = {"grassland"}, + sidelen = 100, + fill_ratio = 0.000009, + y_max = 100, + y_min = 2, + schematic = minetest.get_modpath("more_structures") .. "/schematics/stonehenge_1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = -1, + rotation = "random", +}) + +--Graveyard +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:dirt", "default:dirt_with_grass"}, + biomes = {"deciduous_forest"}, + sidelen = 100, + fill_ratio = 0.000009, + y_max = 100, + y_min = 2, + schematic = minetest.get_modpath("more_structures") .. "/schematics/graveyard_1.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Ruined Hut +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_grass"}, + biomes = {"deciduous_forest"}, + sidelen = 100, + fill_ratio = 0.000006, + y_max = 150, + y_min = 2, + schematic = minetest.get_modpath("more_structures") .. "/schematics/ruined_hut_1.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Spike +minetest.register_node("more_structures:spike", { + description = ("Spike"), + drawtype = "plantlike", + paramtype = "light", + tiles = {"ms_spike.png"}, + inventory_image = "ms_spike.png", + wield_image = "ms_spike.png", + walkable = false, + damage_per_second = 5, + groups = {choppy = 1}, + selection_box = { + type = "fixed", + fixed = {-0.25, -0.5, -0.25, 0.25, 0.375, 0.25}, + }, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_craft({ + output = "more_structures:spike 3", + recipe = { + {"", "", ""}, + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"group:wood", "group:wood", "group:wood"} + } +}) + +--Desert Outposts +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.000002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desert_outpost_1a.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.000002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desert_outpost_1b.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.000002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desert_outpost_2a.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:desert_sand"}, + biomes = {"desert", "sandstone_desert"}, + sidelen = 50, + fill_ratio = 0.000002, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/desert_outpost_2b.mts", + flags = "place_center_x, place_center_z, force_placement", + rotation = "random", +}) + +--Flame Obelisks +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:silver_sand"}, + biomes = {"cold_desert"}, + sidelen = 50, + fill_ratio = 0.000004, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/flame_obelisk.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, +}) + +--Totem Poles +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:dirt_with_rainforest_litter", "default:dry_dirt_with_dry_grass"}, + biomes = {"rainforest", "savanna"}, + sidelen = 50, + fill_ratio = 0.000009, + y_max = 300, + y_min = -5, + schematic = minetest.get_modpath("more_structures") .. "/schematics/totem_pole.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Tradewagon +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone", "default:dirt", "default:dirt_with_grass", "default:dry_dirt_with_dry_grass"}, + biomes = {"grassland", "savanna"}, + sidelen = 100, + fill_ratio = 0.00001, + y_max = 300, + y_min = 7, + schematic = minetest.get_modpath("more_structures") .. "/schematics/tradewagon.mts", + flags = "place_center_x, place_center_z, force_placement", + place_offset_y = 1, + rotation = "random", +}) + +--Fortress +minetest.register_decoration({ + deco_type = "schematic", + place_on = {"default:stone"}, + sidelen = 100, + fill_ratio = 0.000007, + y_max = -512, + y_min = -31000, + schematic = minetest.get_modpath("more_structures") .. "/schematics/fortress.mts", + flags = "place_center_x, place_center_z, force_placement, all_floors", + place_offset_y = -3, + rotation = "random", +}) + +--minetest.register_node("more_structures:villagehouse_1", { +-- description = ("Village House 1"), +-- tiles = {"default_wood.png"}, +-- is_ground_content = false, +-- groups = {choppy = 2, oddly_breakable_by_hand = 2}, +-- on_place = morestructures.place_house(pos) +--}) + +--function morestructures.place_house(pos) +-- local node = minetest.get_node(pos) +-- if node.name == "more_structures:villagehouse_1" then +-- local path = minetest.get_modpath("more_structures") .. "/schematics/villagehouse_1.mts", +-- minetest.place_schematic({x = pos.x, y = pos.y, z = pos.z}, path, "0", nil, true) +-- end +--end \ No newline at end of file diff --git a/mods/more_structures/mod.conf b/mods/more_structures/mod.conf new file mode 100644 index 00000000..4da479a9 --- /dev/null +++ b/mods/more_structures/mod.conf @@ -0,0 +1,9 @@ +mod_name = more_structures +depends = default, xpanes, vessels, doors, beds, stairs, lootchests, lootchests_default +description = This mod adds several new structures to make your Minetest world more alive. +release_version = 1 +author = ClothierEdward +title = More Structures +name = more_structures + +release = 24128 diff --git a/mods/more_structures/schematics/campsite_1.mts b/mods/more_structures/schematics/campsite_1.mts new file mode 100644 index 00000000..d253dbc1 Binary files /dev/null and b/mods/more_structures/schematics/campsite_1.mts differ diff --git a/mods/more_structures/schematics/campsite_2.mts b/mods/more_structures/schematics/campsite_2.mts new file mode 100644 index 00000000..7c8bf828 Binary files /dev/null and b/mods/more_structures/schematics/campsite_2.mts differ diff --git a/mods/more_structures/schematics/campsite_3.mts b/mods/more_structures/schematics/campsite_3.mts new file mode 100644 index 00000000..3a0a8dc1 Binary files /dev/null and b/mods/more_structures/schematics/campsite_3.mts differ diff --git a/mods/more_structures/schematics/desert_outpost_1.mts b/mods/more_structures/schematics/desert_outpost_1.mts new file mode 100644 index 00000000..eb242245 Binary files /dev/null and b/mods/more_structures/schematics/desert_outpost_1.mts differ diff --git a/mods/more_structures/schematics/desert_outpost_1a.mts b/mods/more_structures/schematics/desert_outpost_1a.mts new file mode 100644 index 00000000..327b6f93 Binary files /dev/null and b/mods/more_structures/schematics/desert_outpost_1a.mts differ diff --git a/mods/more_structures/schematics/desert_outpost_1b.mts b/mods/more_structures/schematics/desert_outpost_1b.mts new file mode 100644 index 00000000..82c385b8 Binary files /dev/null and b/mods/more_structures/schematics/desert_outpost_1b.mts differ diff --git a/mods/more_structures/schematics/desert_outpost_2.mts b/mods/more_structures/schematics/desert_outpost_2.mts new file mode 100644 index 00000000..6f55098f Binary files /dev/null and b/mods/more_structures/schematics/desert_outpost_2.mts differ diff --git a/mods/more_structures/schematics/desert_outpost_2a.mts b/mods/more_structures/schematics/desert_outpost_2a.mts new file mode 100644 index 00000000..7de480b1 Binary files /dev/null and b/mods/more_structures/schematics/desert_outpost_2a.mts differ diff --git a/mods/more_structures/schematics/desert_outpost_2b.mts b/mods/more_structures/schematics/desert_outpost_2b.mts new file mode 100644 index 00000000..d7f0438b Binary files /dev/null and b/mods/more_structures/schematics/desert_outpost_2b.mts differ diff --git a/mods/more_structures/schematics/desertsand_arch1.mts b/mods/more_structures/schematics/desertsand_arch1.mts new file mode 100644 index 00000000..a6ddc323 Binary files /dev/null and b/mods/more_structures/schematics/desertsand_arch1.mts differ diff --git a/mods/more_structures/schematics/desertsand_arch2.mts b/mods/more_structures/schematics/desertsand_arch2.mts new file mode 100644 index 00000000..c7b70f3f Binary files /dev/null and b/mods/more_structures/schematics/desertsand_arch2.mts differ diff --git a/mods/more_structures/schematics/desertsand_pillar1.mts b/mods/more_structures/schematics/desertsand_pillar1.mts new file mode 100644 index 00000000..86b51fe8 Binary files /dev/null and b/mods/more_structures/schematics/desertsand_pillar1.mts differ diff --git a/mods/more_structures/schematics/desertsand_pillar2.mts b/mods/more_structures/schematics/desertsand_pillar2.mts new file mode 100644 index 00000000..386d4aa1 Binary files /dev/null and b/mods/more_structures/schematics/desertsand_pillar2.mts differ diff --git a/mods/more_structures/schematics/flame_obelisk.mts b/mods/more_structures/schematics/flame_obelisk.mts new file mode 100644 index 00000000..e37573ec Binary files /dev/null and b/mods/more_structures/schematics/flame_obelisk.mts differ diff --git a/mods/more_structures/schematics/fortress.mts b/mods/more_structures/schematics/fortress.mts new file mode 100644 index 00000000..f1241123 Binary files /dev/null and b/mods/more_structures/schematics/fortress.mts differ diff --git a/mods/more_structures/schematics/graveyard_1.mts b/mods/more_structures/schematics/graveyard_1.mts new file mode 100644 index 00000000..29085e92 Binary files /dev/null and b/mods/more_structures/schematics/graveyard_1.mts differ diff --git a/mods/more_structures/schematics/igloo_1.mts b/mods/more_structures/schematics/igloo_1.mts new file mode 100644 index 00000000..19ba4f7b Binary files /dev/null and b/mods/more_structures/schematics/igloo_1.mts differ diff --git a/mods/more_structures/schematics/igloo_2.mts b/mods/more_structures/schematics/igloo_2.mts new file mode 100644 index 00000000..3bccc7e7 Binary files /dev/null and b/mods/more_structures/schematics/igloo_2.mts differ diff --git a/mods/more_structures/schematics/igloo_3.mts b/mods/more_structures/schematics/igloo_3.mts new file mode 100644 index 00000000..1cb05c0a Binary files /dev/null and b/mods/more_structures/schematics/igloo_3.mts differ diff --git a/mods/more_structures/schematics/igloo_4.mts b/mods/more_structures/schematics/igloo_4.mts new file mode 100644 index 00000000..0249f74a Binary files /dev/null and b/mods/more_structures/schematics/igloo_4.mts differ diff --git a/mods/more_structures/schematics/jungle_temple.mts b/mods/more_structures/schematics/jungle_temple.mts new file mode 100644 index 00000000..762afc4f Binary files /dev/null and b/mods/more_structures/schematics/jungle_temple.mts differ diff --git a/mods/more_structures/schematics/mese_hoard.mts b/mods/more_structures/schematics/mese_hoard.mts new file mode 100644 index 00000000..dce2c9ef Binary files /dev/null and b/mods/more_structures/schematics/mese_hoard.mts differ diff --git a/mods/more_structures/schematics/obsidian_pillar1.mts b/mods/more_structures/schematics/obsidian_pillar1.mts new file mode 100644 index 00000000..73ad1310 Binary files /dev/null and b/mods/more_structures/schematics/obsidian_pillar1.mts differ diff --git a/mods/more_structures/schematics/obsidian_pillar2.mts b/mods/more_structures/schematics/obsidian_pillar2.mts new file mode 100644 index 00000000..f3fd47fa Binary files /dev/null and b/mods/more_structures/schematics/obsidian_pillar2.mts differ diff --git a/mods/more_structures/schematics/ruined_hut_1.mts b/mods/more_structures/schematics/ruined_hut_1.mts new file mode 100644 index 00000000..f0e29fb0 Binary files /dev/null and b/mods/more_structures/schematics/ruined_hut_1.mts differ diff --git a/mods/more_structures/schematics/sand_arch1.mts b/mods/more_structures/schematics/sand_arch1.mts new file mode 100644 index 00000000..0d055938 Binary files /dev/null and b/mods/more_structures/schematics/sand_arch1.mts differ diff --git a/mods/more_structures/schematics/sand_arch2.mts b/mods/more_structures/schematics/sand_arch2.mts new file mode 100644 index 00000000..c065c323 Binary files /dev/null and b/mods/more_structures/schematics/sand_arch2.mts differ diff --git a/mods/more_structures/schematics/sand_pillar1.mts b/mods/more_structures/schematics/sand_pillar1.mts new file mode 100644 index 00000000..0c77a43d Binary files /dev/null and b/mods/more_structures/schematics/sand_pillar1.mts differ diff --git a/mods/more_structures/schematics/sand_pillar2.mts b/mods/more_structures/schematics/sand_pillar2.mts new file mode 100644 index 00000000..93269b16 Binary files /dev/null and b/mods/more_structures/schematics/sand_pillar2.mts differ diff --git a/mods/more_structures/schematics/silversand_arch1.mts b/mods/more_structures/schematics/silversand_arch1.mts new file mode 100644 index 00000000..85958314 Binary files /dev/null and b/mods/more_structures/schematics/silversand_arch1.mts differ diff --git a/mods/more_structures/schematics/silversand_arch2.mts b/mods/more_structures/schematics/silversand_arch2.mts new file mode 100644 index 00000000..76de7271 Binary files /dev/null and b/mods/more_structures/schematics/silversand_arch2.mts differ diff --git a/mods/more_structures/schematics/silversand_pillar1.mts b/mods/more_structures/schematics/silversand_pillar1.mts new file mode 100644 index 00000000..687ec50f Binary files /dev/null and b/mods/more_structures/schematics/silversand_pillar1.mts differ diff --git a/mods/more_structures/schematics/silversand_pillar2.mts b/mods/more_structures/schematics/silversand_pillar2.mts new file mode 100644 index 00000000..eb434f81 Binary files /dev/null and b/mods/more_structures/schematics/silversand_pillar2.mts differ diff --git a/mods/more_structures/schematics/stonehenge_1.mts b/mods/more_structures/schematics/stonehenge_1.mts new file mode 100644 index 00000000..77f65959 Binary files /dev/null and b/mods/more_structures/schematics/stonehenge_1.mts differ diff --git a/mods/more_structures/schematics/totem_pole.mts b/mods/more_structures/schematics/totem_pole.mts new file mode 100644 index 00000000..730fc6cf Binary files /dev/null and b/mods/more_structures/schematics/totem_pole.mts differ diff --git a/mods/more_structures/schematics/tower_1.mts b/mods/more_structures/schematics/tower_1.mts new file mode 100644 index 00000000..3792ea95 Binary files /dev/null and b/mods/more_structures/schematics/tower_1.mts differ diff --git a/mods/more_structures/schematics/tradewagon.mts b/mods/more_structures/schematics/tradewagon.mts new file mode 100644 index 00000000..daf725eb Binary files /dev/null and b/mods/more_structures/schematics/tradewagon.mts differ diff --git a/mods/more_structures/schematics/villagehouse_1.mts b/mods/more_structures/schematics/villagehouse_1.mts new file mode 100644 index 00000000..9db7228c Binary files /dev/null and b/mods/more_structures/schematics/villagehouse_1.mts differ diff --git a/mods/more_structures/schematics/well_1.mts b/mods/more_structures/schematics/well_1.mts new file mode 100644 index 00000000..6d840879 Binary files /dev/null and b/mods/more_structures/schematics/well_1.mts differ diff --git a/mods/more_structures/schematics/well_2.mts b/mods/more_structures/schematics/well_2.mts new file mode 100644 index 00000000..64061170 Binary files /dev/null and b/mods/more_structures/schematics/well_2.mts differ diff --git a/mods/more_structures/schematics/well_3.mts b/mods/more_structures/schematics/well_3.mts new file mode 100644 index 00000000..3510c1f2 Binary files /dev/null and b/mods/more_structures/schematics/well_3.mts differ diff --git a/mods/more_structures/schematics/well_4.mts b/mods/more_structures/schematics/well_4.mts new file mode 100644 index 00000000..09874a27 Binary files /dev/null and b/mods/more_structures/schematics/well_4.mts differ diff --git a/mods/more_structures/screenshot.png b/mods/more_structures/screenshot.png new file mode 100644 index 00000000..8c7c7fe4 Binary files /dev/null and b/mods/more_structures/screenshot.png differ diff --git a/mods/more_structures/textures/ms_spike.png b/mods/more_structures/textures/ms_spike.png new file mode 100644 index 00000000..e950ebe0 Binary files /dev/null and b/mods/more_structures/textures/ms_spike.png differ diff --git a/mods/mtg_plus/README.md b/mods/mtg_plus/README.md new file mode 100644 index 00000000..3277e5d9 --- /dev/null +++ b/mods/mtg_plus/README.md @@ -0,0 +1,45 @@ +# Minetest Game Plus [`mtg_plus`] +This mods adds various simple decorative blocks, doors, panes and more to extend Minetest Game. +The focus lies on: + +* Snow and ice +* Panes +* Doors +* Metals + +Current version: 1.1.2 +For Minetest/Minetest Game 5.1.0 or later + +## Special stuff +Most new items are purely decorational and self-explanationary, but a few items are somewhat unique: + +* Dirty Glass: Like glass, but sunlight will not go through unlimited +* Aggregated Diamond Block: Not even a diamond pickaxe can break it. But TNT will do +* Ice-themed doors and trapdoors: Solid doors for which (like for ice) light goes through +* Goldwood: Fireproof wood + +## Compability +This mod is optionally compatible with: + +* `xdecor`: For adding more recipes for compability +* `moreblocks`: For adding recipes involving the sweeper +* `furniture`: For adding furniture for goldwood and the cobblestone variants +* `doc_items`: For providing item descriptions (incomplete) +* `awards`: More achievements + +## Credits and licenses +This mod is free software. + +### Code +* Author: Wuzzy +* Licensed under: MIT License + +### Graphics +All graphics are derivate works of Minetest Game textures. +They are licensed under [CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/). + +### Sounds +* `mtg_plus_door_icesteel_close.ogg`: Derivate work of `door_steel_door_close.ogg` by HazMatt from Minetest Game, licensed under [CC-BY 3.0](https://creativecommons.org/licenses/by/3.0/) +* `mtg_plus_door_icesteel_open.ogg`: Derivate work of `door_steel_door_open.ogg` by HazMatt from Minetest Game, licensed under CC-BY 3.0 +* Everything else is licensed under the MIT License. + diff --git a/mods/mtg_plus/aliases.lua b/mods/mtg_plus/aliases.lua new file mode 100644 index 00000000..f51c303a --- /dev/null +++ b/mods/mtg_plus/aliases.lua @@ -0,0 +1,12 @@ +-- Legacy aliases +-- Earlier versions had beds, but those were removed because they were considered ugly +minetest.register_alias("mtg_plus:gold_bed_bottom", "beds:fancy_bed_bottom") +minetest.register_alias("mtg_plus:gold_bed_top", "beds:fancy_bed_top") +minetest.register_alias("mtg_plus:bed_simple_white_bottom", "beds:bed_bottom") +minetest.register_alias("mtg_plus:bed_simple_white_top", "beds:bed_top") +minetest.register_alias("mtg_plus:goldapple", "default:apple") +minetest.register_alias("xpanes:obsidian_glass_flat", "xpanes:obsidian_pane_flat") +minetest.register_alias("xpanes:obsidian_glass", "xpanes:obsidian_pane") +minetest.register_alias("doors:door_iron_bar", "xpanes:door_steel_bar") +minetest.register_alias("doors:door_iron_bar_a", "xpanes:door_steel_bar_a") +minetest.register_alias("doors:door_iron_bar_b", "xpanes:door_steel_bar_b") diff --git a/mods/mtg_plus/awards.lua b/mods/mtg_plus/awards.lua new file mode 100644 index 00000000..059df2b2 --- /dev/null +++ b/mods/mtg_plus/awards.lua @@ -0,0 +1,94 @@ +local S = minetest.get_translator("mtg_plus") + +if minetest.get_modpath("awards") then + awards.register_achievement("mtg_plus_goldwood", { + title = S("Rich Carpenter"), + description = S("Craft 100 goldwood."), + icon = "mtg_plus_goldwood.png", + trigger = { + type = "craft", + item = "mtg_plus:goldwood", + target = 100 + } + }) + awards.register_achievement("mtg_plus_gravel_cobble", { + title = S("Historic City"), + description = S("Craft 400 cobbled gravel."), + icon = "mtg_plus_gravel_cobble.png", + trigger = { + type = "craft", + item = "mtg_plus:gravel_cobble", + target = 400 + } + }) + awards.register_achievement("mtg_plus_papyrus_block", { + title = S("Papyrus Panic"), + description = S("Build 100 papyrus blocks."), + icon = "mtg_plus_papyrus_block_side.png", + trigger = { + type = "place", + node = "mtg_plus:papyrus_block", + target = 100 + } + }) + awards.register_achievement("mtg_plus_harddiamondblock", { + title = S("Can't dig me!"), + description = S("Place a aggregated diamond block."), + icon = "mtg_plus_hard_diamond_block.png", + trigger = { + type = "place", + node = "mtg_plus:harddiamondblock", + target = 1 + } + }) + awards.register_achievement("mtg_plus_ice_tile16", { + title = S("Ice Crazy"), + description = S("Craft 128 dense ice tiles."), + icon = "mtg_plus_ice_tile16.png", + trigger = { + type = "craft", + item = "mtg_plus:ice_tile16", + target = 128 + } + }) + awards.register_achievement("mtg_plus_hard_snow_brick", { + title = S("Let's build an igloo!"), + description = S("Place 225 hard snow bricks."), + icon = "mtg_plus_hard_snow_brick.png", + trigger = { + type = "place", + node = "mtg_plus:hard_snow_brick", + target = 225 + } + }) + awards.register_achievement("mtg_plus_gold_bed", { + title = S("Luxurious Adornment"), + description = S("Craft 4 small gold-framed diamond blocks."), + icon = "mtg_plus_gold_diamond_block.png", + trigger = { + type = "craft", + item = "mtg_plus:gold_diamond_block", + target = 4 + } + }) + awards.register_achievement("mtg_plus_jungle_cobble", { + title = S("Green Jungle"), + description = S("Craft 512 jungle cobblestone."), + icon = "mtg_plus_jungle_cobble.png", + trigger = { + type = "craft", + item = "mtg_plus:jungle_cobble", + target = 512 + } + }) + awards.register_achievement("mtg_plus_sandstone_cobble", { + title = S("Yellow Desert"), + description = S("Craft 512 cobbled sandstone."), + icon = "mtg_plus_sandstone_cobble.png", + trigger = { + type = "craft", + item = "mtg_plus:sandstone_cobble", + target = 512 + } + }) +end diff --git a/mods/mtg_plus/brickblocks.lua b/mods/mtg_plus/brickblocks.lua new file mode 100644 index 00000000..ca4a0557 --- /dev/null +++ b/mods/mtg_plus/brickblocks.lua @@ -0,0 +1,419 @@ +local S = minetest.get_translator("mtg_plus") + +local deco, build +if minetest.get_modpath("doc_items") then + deco = doc.sub.items.temp.deco + build = doc.sub.items.temp.build +end + +local metal_sounds +if default.node_sound_metal_defaults then + metal_sounds = default.node_sound_metal_defaults() +else + metal_sounds = default.node_sound_stone_defaults() +end + + +-- Dirt bricks + +minetest.register_node("mtg_plus:dirtbrick", { + description = S("Soft Dirt Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_dirt_brick.png"}, + is_ground_content = false, + groups = { crumbly = 2, soil = 1 }, + sounds = default.node_sound_dirt_defaults(), + drop = "default:dirt", +}) + +minetest.register_craft({ + output = "mtg_plus:dirtbrick 4", + recipe = { { "default:dirt", "default:dirt", }, + { "default:dirt", "default:dirt", }, }, +}) + +minetest.register_node("mtg_plus:harddirtbrick", { + description = S("Hardened Dirt Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_dirt_brick_hard.png"}, + is_ground_content = false, + groups = { crumbly = 1, level = 1, soil = 1 }, + sounds = default.node_sound_dirt_defaults(), +}) + +minetest.register_craft({ + type = "cooking", + output = "mtg_plus:harddirtbrick", + recipe = "mtg_plus:dirtbrick", + cooktime = 5, +}) + +-- Metal bricks + +minetest.register_node("mtg_plus:goldbrick", { + description = S("Gold Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_gold_brick.png"}, + is_ground_content = false, + groups = { cracky = 1, }, + sounds = metal_sounds, +}) + +minetest.register_craft({ + output = "mtg_plus:goldbrick 4", + recipe = { { "default:goldblock", "default:goldblock", }, + { "default:goldblock", "default:goldblock", }, }, +}) + +minetest.register_node("mtg_plus:bronzebrick", { + description = S("Bronze Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_bronze_brick.png"}, + is_ground_content = false, + groups = { cracky = 1, level = 2 }, + sounds = metal_sounds, +}) + +minetest.register_craft({ + output = "mtg_plus:bronzebrick 4", + recipe = { { "default:bronzeblock", "default:bronzeblock", }, + { "default:bronzeblock", "default:bronzeblock", }, }, +}) + +minetest.register_node("mtg_plus:tinbrick", { + description = S("Tin Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_tin_brick.png"}, + is_ground_content = false, + groups = { cracky = 1, level = 2 }, + sounds = metal_sounds, +}) + +minetest.register_craft({ + output = "mtg_plus:tinbrick 4", + recipe = { { "default:tinblock", "default:tinblock", }, + { "default:tinblock", "default:tinblock", }, }, +}) + +minetest.register_node("mtg_plus:copperbrick", { + description = S("Copper Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_copper_brick.png"}, + is_ground_content = false, + groups = { cracky = 1, level = 2 }, + sounds = metal_sounds, +}) + +minetest.register_craft({ + output = "mtg_plus:copperbrick 4", + recipe = { { "default:copperblock", "default:copperblock", }, + { "default:copperblock", "default:copperblock", }, }, +}) + +minetest.register_node("mtg_plus:steelbrick", { + description = S("Steel Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_steel_brick.png"}, + is_ground_content = false, + groups = { cracky = 1, level = 2 }, + sounds = metal_sounds, +}) + +minetest.register_craft({ + output = "mtg_plus:steelbrick 4", + recipe = { { "default:steelblock", "default:steelblock", }, + { "default:steelblock", "default:steelblock", }, }, +}) + + +-- Golden edges + +minetest.register_node("mtg_plus:stonebrick_gold", { + description = S("Stone Brick with Golden Edges"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_stone_brick_gold.png"}, + is_ground_content = false, + groups = { cracky = 2, stone = 1 }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:stonebrick_gold 4", + recipe = { { "", "default:stonebrick", "", }, + { "default:stonebrick", "default:gold_ingot", "default:stonebrick", }, + { "", "default:stonebrick", "", } } +}) + +minetest.register_node("mtg_plus:desert_stonebrick_gold", { + description = S("Desert Stone Brick with Golden Edges"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_desert_stone_brick_gold.png"}, + is_ground_content = false, + groups = { cracky = 2, stone = 1 }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:desert_stonebrick_gold 4", + recipe = { { "", "default:desert_stonebrick", "" }, + { "default:desert_stonebrick", "default:gold_ingot", "default:desert_stonebrick", }, + { "", "default:desert_stonebrick", "", } } +}) + +minetest.register_node("mtg_plus:sandstonebrick_gold", { + description = S("Sandstone Brick with Golden Edges"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_sandstone_brick_gold.png"}, + is_ground_content = false, + groups = { cracky = 2, }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:sandstonebrick_gold 4", + recipe = { { "", "default:sandstonebrick", "", }, + { "default:sandstonebrick", "default:gold_ingot", "default:sandstonebrick", }, + { "", "default:sandstonebrick", "", } } +}) + +minetest.register_node("mtg_plus:desert_sandstone_brick_gold", { + description = S("Desert Sandstone Brick with Golden Edges"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_desert_sandstone_brick_gold.png"}, + is_ground_content = false, + groups = { cracky = 2, }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:desert_sandstone_brick_gold 4", + recipe = { { "", "default:desert_sandstone_brick", "", }, + { "default:desert_sandstone_brick", "default:gold_ingot", "default:desert_sandstone_brick", }, + { "", "default:desert_sandstone_brick", "", } } +}) + +minetest.register_node("mtg_plus:silver_sandstone_brick_gold", { + description = S("Silver Sandstone Brick with Golden Edges"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_silver_sandstone_brick_gold.png"}, + is_ground_content = false, + groups = { cracky = 2, }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:silver_sandstone_brick_gold 4", + recipe = { { "", "default:silver_sandstone_brick", "", }, + { "default:silver_sandstone_brick", "default:gold_ingot", "default:silver_sandstone_brick", }, + { "", "default:silver_sandstone_brick", "", } } +}) + +minetest.register_node("mtg_plus:obsidianbrick_gold", { + description = S("Obsidian Brick with Golden Edges"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_obsidian_brick_gold.png"}, + is_ground_content = false, + groups = { cracky = 1, level = 2 }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:obsidianbrick_gold 4", + recipe = { { "", "default:obsidianbrick", "", }, + { "default:obsidianbrick", "default:gold_ingot", "default:obsidianbrick", }, + { "", "default:obsidianbrick", "", } } +}) + + +-- Snow and ice + +minetest.register_node("mtg_plus:ice_block", { + description = S("Ice Block"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_ice_block.png"}, + groups = {cracky = 3, cools_lava = 1, slippery = 3 }, + is_ground_content = false, + paramtype = "light", + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:ice_block 9", + recipe = { { "default:ice", "default:ice", "default:ice" }, + { "default:ice", "default:ice", "default:ice" }, + { "default:ice", "default:ice", "default:ice" } } +}) + + + +minetest.register_node("mtg_plus:ice_tile4", { + description = S("Ice Tile"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_ice_tile4.png"}, + groups = {cracky = 3, level = 1, cools_lava = 1, slippery = 3 }, + is_ground_content = false, + paramtype = "light", + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:ice_tile4", + recipe = { { "mtg_plus:ice_block", "mtg_plus:ice_block" }, + { "mtg_plus:ice_block", "mtg_plus:ice_block" },} +}) + +minetest.register_node("mtg_plus:ice_tile16", { + description = S("Dense Ice Tile"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_ice_tile16.png"}, + groups = {cracky = 3, level = 2, cools_lava = 1, slippery = 2 }, + is_ground_content = false, + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:ice_tile16", + recipe = { { "mtg_plus:ice_tile4", "mtg_plus:ice_tile4" }, + { "mtg_plus:ice_tile4", "mtg_plus:ice_tile4" } } +}) + +minetest.register_node("mtg_plus:snow_brick", { + description = S("Soft Snow Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_snow_brick.png"}, + groups = {crumbly = 2, cools_lava = 1, snowy = 1}, + is_ground_content = false, + sounds = default.node_sound_dirt_defaults({ + footstep={name="default_snow_footstep", gain = 0.15}, + dig={name="default_snow_footstep", gain = 0.2}, + dug={name="default_snow_footstep", gain = 0.2} + }), +}) + +minetest.register_craft({ + output = "mtg_plus:snow_brick 4", + recipe = { { "default:snowblock", "default:snowblock" }, + { "default:snowblock", "default:snowblock", } }, +}) + +minetest.register_craft({ + output = "default:snowblock", + recipe = { { "mtg_plus:snow_brick" } }, +}) + +minetest.register_node("mtg_plus:hard_snow_brick", { + description = S("Hard Snow Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_hard_snow_brick.png"}, + groups = {crumbly = 1, cracky = 2, cools_lava = 1, snowy = 1}, + is_ground_content = false, + sounds = default.node_sound_dirt_defaults({ + dig={name="default_snow_footstep", gain = 0.2}, + dug={name="default_snow_footstep", gain = 0.2} + }), +}) + +minetest.register_craft({ + output = "mtg_plus:hard_snow_brick", + recipe = { { "mtg_plus:snow_brick", "mtg_plus:snow_brick" }, + { "mtg_plus:snow_brick", "mtg_plus:snow_brick" } }, +}) + +minetest.register_node("mtg_plus:ice_snow_brick", { + description = S("Icy Snow Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_ice_snow_brick.png"}, + groups = {cracky = 2, cools_lava = 1, slippery=1}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:ice_snow_brick 2", + type = "shapeless", + recipe = { "mtg_plus:hard_snow_brick", "mtg_plus:ice_brick" }, +}) + +minetest.register_node("mtg_plus:ice_brick", { + description = S("Ice Brick"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_ice_brick.png"}, + paramtype = "light", + groups = {cracky = 3, cools_lava = 1, slippery = 3}, + is_ground_content = false, + sounds = default.node_sound_glass_defaults(), +}) + +-- Papyrus Block + +minetest.register_node("mtg_plus:papyrus_block", { + description = S("Papyrus Block"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_papyrus_block_y.png","mtg_plus_papyrus_block_y.png","mtg_plus_papyrus_block_side2.png","mtg_plus_papyrus_block_side2.png","mtg_plus_papyrus_block_side.png","mtg_plus_papyrus_block_side.png"}, + groups = {snappy = 2, choppy = 2, flammable = 3}, + is_ground_content = false, + sounds = default.node_sound_leaves_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:papyrus_block", + recipe = { { "default:papyrus", "default:papyrus", "default:papyrus", }, + { "default:papyrus", "default:papyrus", "default:papyrus", }, + { "default:papyrus", "default:papyrus", "default:papyrus", } } +}) + +minetest.register_craft({ + output = "default:papyrus 9", + recipe = { { "mtg_plus:papyrus_block" } } +}) + +minetest.register_craft({ + type = "fuel", + recipe = "mtg_plus:papyrus_block", + burntime = 9, +}) + + +-- Flint block + +minetest.register_node("mtg_plus:flint_block", { + description = S("Flint Block"), + _doc_items_longdesc = deco, + tiles = {"mtg_plus_flint_block.png"}, + is_ground_content = false, + groups = {cracky = 2}, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:flint_block", + recipe = { + { "default:flint", "default:flint", "default:flint" }, + { "default:flint", "default:flint", "default:flint" }, + { "default:flint", "default:flint", "default:flint" }, + } +}) + +minetest.register_craft({ + output = "default:flint 9 ", + recipe = { + { "mtg_plus:flint_block" }, + } +}) + +-- Gold-framed diamond block, just an absurd luxurious decoration. :D +minetest.register_node("mtg_plus:gold_diamond_block", { + description = S("Small Gold-framed Diamond Block"), + _doc_items_longdesc = deco, + tiles = {"mtg_plus_gold_diamond_block.png"}, + is_ground_content = false, + groups = {cracky = 1, level = 3}, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:gold_diamond_block", + recipe = { { "default:gold_ingot", "default:diamond", "default:gold_ingot", }, + { "default:diamond", "default:diamond", "default:diamond" }, + { "default:gold_ingot", "default:diamond", "default:gold_ingot", } }, +}) diff --git a/mods/mtg_plus/cobble.lua b/mods/mtg_plus/cobble.lua new file mode 100644 index 00000000..beef4e09 --- /dev/null +++ b/mods/mtg_plus/cobble.lua @@ -0,0 +1,117 @@ +local S = minetest.get_translator("mtg_plus") + +local build +if minetest.get_modpath("doc_items") then + build = doc.sub.items.temp.build +end + +-- Cobblestone + +minetest.register_node("mtg_plus:gravel_cobble", { + description = S("Cobbled Gravel"), + _doc_items_longdesc = S("Cobbled gravel is solidified gravel, carefully arranged in a mosaic-like pattern. It makes a nice building material."), + tiles = {"mtg_plus_gravel_cobble.png"}, + is_ground_content = false, + groups = { cracky = 3, stone = 1 }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:gravel_cobble 2", + recipe = { { "default:gravel", "default:gravel" }, + { "default:gravel", "default:gravel" } }, +}) + +minetest.register_craft({ + type = "cooking", + output = "default:gravel", + recipe = "mtg_plus:gravel_cobble", +}) + + +minetest.register_node("mtg_plus:sandstone_cobble", { + description = S("Cobbled Sandstone"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_sandstone_cobble.png"}, + groups = {cracky = 3, }, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:sandstone_cobble 2", + recipe = { { "default:sandstone", "default:sandstone" } }, +}) + +minetest.register_craft({ + type = "cooking", + output = "default:sandstone", + recipe = "mtg_plus:sandstone_cobble", +}) + +minetest.register_node("mtg_plus:desert_sandstone_cobble", { + description = S("Cobbled Desert Sandstone"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_desert_sandstone_cobble.png"}, + groups = {cracky = 3, }, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:desert_sandstone_cobble 2", + recipe = { { "default:desert_sandstone", "default:desert_sandstone" } }, +}) + +minetest.register_craft({ + type = "cooking", + output = "default:desert_sandstone", + recipe = "mtg_plus:desert_sandstone_cobble", +}) + +minetest.register_node("mtg_plus:silver_sandstone_cobble", { + description = S("Cobbled Silver Sandstone"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_silver_sandstone_cobble.png"}, + groups = {cracky = 3, }, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:silver_sandstone_cobble 2", + recipe = { { "default:silver_sandstone", "default:silver_sandstone" } }, +}) + +minetest.register_craft({ + type = "cooking", + output = "default:silver_sandstone", + recipe = "mtg_plus:silver_sandstone_cobble", +}) + +minetest.register_node("mtg_plus:jungle_cobble", { + description = S("Jungle Cobblestone"), + _doc_items_longdesc = build, + tiles = {"mtg_plus_jungle_cobble.png"}, + groups = {cracky=3, stone=1}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:jungle_cobble", + type = "shapeless", + recipe = { "default:jungleleaves", "default:jungleleaves", "default:cobble" }, +}) + +minetest.register_craft({ + output = "mtg_plus:jungle_cobble", + type = "shapeless", + recipe = { "default:jungleleaves", "default:mossycobble" }, +}) + +minetest.register_craft({ + output = "default:stone", + type = "cooking", + recipe = "mtg_plus:jungle_cobble", +}) diff --git a/mods/mtg_plus/compat_xdecor.lua b/mods/mtg_plus/compat_xdecor.lua new file mode 100644 index 00000000..1aae36bb --- /dev/null +++ b/mods/mtg_plus/compat_xdecor.lua @@ -0,0 +1,47 @@ +if minetest.get_modpath("xdecor") then + minetest.register_craft({ + output = "xpanes:papyrus 2", + recipe = { + { "", "farming:string", "" }, + { "farming:string", "xpanes:bamboo_frame", "farming:string" }, + } + }) + + minetest.register_craft({ + output = "xpanes:bamboo_frame", + recipe = { { "xpanes:papyrus", "xpanes:papyrus", }, } + }) + + -- xdecor compability + minetest.register_craft({ + output = "xdecor:packed_ice 2", + recipe = { { "mtg_plus:ice_block", "mtg_plus:ice_block", "mtg_plus:ice_block" }, + { "mtg_plus:ice_block", "", "mtg_plus:ice_block" }, + { "mtg_plus:ice_block", "mtg_plus:ice_block", "mtg_plus:ice_block" }, }, + }) + + minetest.register_craft({ + output = "mtg_plus:ice_tile16", + recipe = { { "xdecor:packed_ice", "xdecor:packed_ice" }, + { "xdecor:packed_ice", "xdecor:packed_ice" }, }, + }) + + -- Alternate ice brick crafting recipe for xdecor compability + minetest.register_craft({ + output = "mtg_plus:ice_brick 4", + recipe = { { "", "default:ice", "default:ice" }, + { "default:ice", "default:ice", "" } }, + }) + minetest.register_craft({ + output = "mtg_plus:ice_brick 4", + recipe = { { "default:ice", "default:ice", "" }, + { "", "default:ice", "default:ice" } }, + }) +else + -- Normal ice brick crafting recipe + minetest.register_craft({ + output = "mtg_plus:ice_brick 4", + recipe = { { "default:ice", "default:ice" }, + { "default:ice", "default:ice" } }, + }) +end \ No newline at end of file diff --git a/mods/mtg_plus/doors.lua b/mods/mtg_plus/doors.lua new file mode 100644 index 00000000..c22c2364 --- /dev/null +++ b/mods/mtg_plus/doors.lua @@ -0,0 +1,156 @@ +local S = minetest.get_translator("mtg_plus") + +local door_simple = S("A door covers a vertical area of two blocks to block the way. It can be opened and closed by any player.") +local door_simple_use = S("Use the use key on it to open or close it.") + +local metal_sounds +if default.node_sound_metal_defaults then + metal_sounds = default.node_sound_metal_defaults() +else + metal_sounds = default.node_sound_stone_defaults() +end + +-- Doors +doors.register("door_wood_bar", { + tiles = {{ name = "mtg_plus_door_wood_bar.png", backface_culling = true }}, + description = S("Wooden Bar Door"), + _doc_items_longdesc = door_simple, + _doc_items_usagehelp = door_simple_use, + _doc_items_image = "mtg_plus_door_wood_bar_item.png", + inventory_image = "mtg_plus_door_wood_bar_item.png", + sounds = default.node_sound_wood_defaults(), + sound_open = "doors_fencegate_open", + sound_close = "doors_fencegate_close", + groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 }, + recipe = { + {"xpanes:wood_flat",}, + {"xpanes:wood_flat",}, + } +}) + +minetest.register_craft({ + type = "fuel", + recipe = "doors:door_wood_bar", + burntime = 4, +}) + +doors.register("door_papyrus", { + tiles = {{ name = "mtg_plus_door_papyrus.png", backface_culling = true }}, + description = S("Papyrus Door"), + _doc_items_longdesc = door_simple, + _doc_items_usagehelp = door_simple_use, + _doc_items_image = "mtg_plus_door_papyrus_item.png", + inventory_image = "mtg_plus_door_papyrus_item.png", + sounds = default.node_sound_leaves_defaults(), + sound_open = "doors_fencegate_open", + sound_close = "doors_fencegate_close", + groups = { snappy = 2, choppy = 1, flammable = 2 }, + recipe = { + {"default:papyrus", "default:papyrus"}, + {"farming:string", "farming:string"}, + {"default:papyrus", "default:papyrus"}, + } +}) + +minetest.register_craft({ + type = "fuel", + recipe = "doors:door_papyrus", + burntime = 4, +}) + +doors.register("door_ice", { + tiles = {{ name = "mtg_plus_door_ice.png", backface_culling = true }}, + description = S("Ice Door"), + _doc_items_longdesc = S("Ice doors can be opened and closed. They are solid, but some of the light hitting ice doors can still go through, making them an interesting decoration in icy areas."), + _doc_items_usagehelp = door_simple_use, + _doc_items_image = "mtg_plus_door_ice_item.png", + inventory_image = "mtg_plus_door_ice_item.png", + groups = { cracky = 3, slippery = 3, }, + sounds = default.node_sound_glass_defaults(), + sound_open = "mtg_plus_door_ice_open", + sound_close = "mtg_plus_door_ice_close", + recipe = { + {"default:ice", "default:ice"}, + {"default:ice", "default:ice"}, + {"default:ice", "default:ice"}, + } +}) + +doors.register("door_icesteel", { + tiles = {{ name = "mtg_plus_door_icesteel.png", backface_culling = true }}, + description = S("Icy Steel Door"), + _doc_items_longdesc = S("Icy steel doors are a combination of ice doors and steel doors which can only be opened and closed by their owners. They are solid, but some of the light hitting icy steel doors can still go through."), + _doc_items_usagehelp = S("Point the door to see who owns it. Use the use key on the door to open or close it (if you own it)."), + _doc_items_image = "mtg_plus_door_icesteel_item.png", + protected = true, + sound_open = "mtg_plus_door_icesteel_open", + sound_close = "mtg_plus_door_icesteel_close", + inventory_image = "mtg_plus_door_icesteel_item.png", + sounds = metal_sounds, + groups = { snappy = 1, bendy = 2, cracky = 3, melty = 3, level = 2, slippery = 1, }, +}) + +minetest.register_craft({ + output = "doors:door_icesteel 2", + type = "shapeless", + recipe = {"doors:door_ice", "doors:door_steel"}, +}) + +doors.register_fencegate("mtg_plus:gate_goldwood", { + description = S("Goldwood Fence Gate"), + material = "mtg_plus:goldwood", + texture = "mtg_plus_goldwood.png", + groups = {choppy=2, }, +}) + +minetest.override_item("mtg_plus:gate_goldwood_closed", { + _doc_items_longdesc = S("A fence gate made from precious goldwood. It blocks the path, but it can be opened and easily jumped over. Other fence posts will neatly connect to this fence gate."), + _doc_items_usagehelp = S("Right-click the gate to open or close it."), +}) +minetest.override_item("mtg_plus:gate_goldwood_open", { + _doc_items_create_entry = false, +}) +if minetest.get_modpath("doc") then + doc.add_entry_alias("nodes", "mtg_plus:gate_goldwood_closed", "nodes", "mtg_plus:gate_goldwood_open") +end + +doors.register_trapdoor("mtg_plus:trapdoor_ice", { + description = S("Ice Trapdoor"), + _doc_items_longdesc = S("An ice trapdoor covers the floor and can be opened and closed by anyone. Ice trapdoors are solid, but some light can pass through nonetheless."), + _doc_items_usagehelp = door_simple_use, + tile_front = "mtg_plus_trapdoor_ice.png", + tile_side = "mtg_plus_trapdoor_ice_side.png", + inventory_image = "mtg_plus_trapdoor_ice.png", + wield_image = "mtg_plus_trapdoor_ice.png", + sound_open = "mtg_plus_door_ice_open", + sound_close = "mtg_plus_door_ice_close", + sounds = default.node_sound_glass_defaults(), + groups = { cracky = 3, slippery = 3, door = 1 }, +}) + +minetest.register_craft({ + output = "mtg_plus:trapdoor_ice 2", + recipe = { { "default:ice", "default:ice", "default:ice" }, + { "default:ice", "default:ice", "default:ice" }, } +}) + +doors.register_trapdoor("mtg_plus:trapdoor_icesteel", { + description = S("Icy Steel Trapdoor"), + _doc_items_longdesc = S("An icy steel trapdoor is a combination of an ice trapdoor and a steel trapdoor. It covers the floor and can only be opened and closed by its placer. Icy steel trapdoors are solid, but some light can pass through nonetheless."), + _doc_items_usagehelp = S("Point the icy steel trapdoor to see who owns it. Use the use key on it to open or close it (if you own it)."), + protected = true, + tile_front = "mtg_plus_trapdoor_icesteel.png", + tile_side = "mtg_plus_trapdoor_icesteel_side.png", + inventory_image = "mtg_plus_trapdoor_icesteel.png", + wield_image = "mtg_plus_trapdoor_icesteel.png", + sound_open = "mtg_plus_door_icesteel_open", + sound_close = "mtg_plus_door_icesteel_close", + sounds = metal_sounds, + groups = { snappy = 1, bendy = 2, cracky = 3, melty = 3, level = 2, slippery = 1, door = 1 }, +}) + +minetest.register_craft({ + type = "shapeless", + output = "mtg_plus:trapdoor_icesteel 2", + recipe = { "mtg_plus:trapdoor_ice", "doors:trapdoor_steel" }, +}) diff --git a/mods/mtg_plus/extras.lua b/mods/mtg_plus/extras.lua new file mode 100644 index 00000000..c7365b5d --- /dev/null +++ b/mods/mtg_plus/extras.lua @@ -0,0 +1,64 @@ +local S = minetest.get_translator("mtg_plus") + +-- Add additional crafting recipes and nodes if particular mods were detected + +-- moreblocks extras +if minetest.get_modpath("moreblocks") then + minetest.register_craft({ + type = "shapeless", + output = "default:glass", + recipe = {"mtg_plus:dirty_glass", "moreblocks:sweeper"} + }) + minetest.register_craft({ + type = "shapeless", + output = "moreblocks:clean_glass", + recipe = {"mtg_plus:dirty_glass", "moreblocks:sweeper", "moreblocks:sweeper"} + }) + minetest.register_craft({ + output = "mtg_plus:dirty_glass", + recipe = { + {"default:dirt"}, + {"moreblocks:clean_glass"} + } + }) + minetest.register_craft({ + type = "shapeless", + output = "mtg_plus:ice_window", + recipe = { "mtg_plus:ice_tile4", "moreblocks:sweeper" }, + }) +end + +-- furniture extras +if minetest.get_modpath("furniture") then + furniture.register_wooden("mtg_plus:goldwood", { + description = S("Goldwood"), + description_chair = S("Goldwood Chair"), + description_stool = S("Goldwood Stool"), + description_table = S("Goldwood Table"), + }) + furniture.register_stone("mtg_plus:jungle_cobble", { + description = S("Jungle Cobblestone"), + description_stool = S("Jungle Cobblestone Stool"), + description_table = S("Jungle Cobblestone Table"), + }) + furniture.register_stone("mtg_plus:sandstone_cobble", { + description = S("Cobbled Sandstone"), + description_stool = S("Cobbled Sandstone Stool"), + description_table = S("Cobbled Sandstone Table"), + }) + furniture.register_stone("mtg_plus:desert_sandstone_cobble", { + description = S("Cobbled Desert Sandstone"), + description_stool = S("Cobbled Desert Sandstone Stool"), + description_table = S("Cobbled Desert Sandstone Table"), + }) + furniture.register_stone("mtg_plus:silver_sandstone_cobble", { + description = S("Cobbled Silver Sandstone"), + description_stool = S("Cobbled Silver Sandstone Stool"), + description_table = S("Cobbled Silver Sandstone Table"), + }) + furniture.register_stone("mtg_plus:gravel_cobble", { + description = S("Cobbled Gravel"), + description_stool = S("Cobbled Gravel Stool"), + description_table = S("Cobbled Gravel Table"), + }) +end diff --git a/mods/mtg_plus/init.lua b/mods/mtg_plus/init.lua new file mode 100644 index 00000000..0aed8ccd --- /dev/null +++ b/mods/mtg_plus/init.lua @@ -0,0 +1,24 @@ +mtg_plus = {} + +local path = minetest.get_modpath(minetest.get_current_modname()) + +-- Trivial blocks (full definition included; almost all nodes are full cubes) +dofile(path.."/brickblocks.lua") -- Bricks and blocks +dofile(path.."/cobble.lua") -- Cobblestone +dofile(path.."/seethrough.lua") -- Glasslike nodes +dofile(path.."/specials.lua") -- Nodes with something special (or misc. blocks) + +-- Non-trivial blocks (definition require API) +dofile(path.."/stairslabs.lua") -- Stairs and slabs +dofile(path.."/wallfences.lua") -- Walls and fences +dofile(path.."/xpanes.lua") -- Panes (xpanes mod) +dofile(path.."/doors.lua") -- Doors and trapdoors +dofile(path.."/ladders.lua") -- Ladders + +-- Support for other mods +dofile(path.."/extras.lua") -- Additional blocks and crafts for optional mods +dofile(path.."/awards.lua") -- Achievements for the awards mod + +-- Compability +dofile(path.."/compat_xdecor.lua") -- xdecor compability +dofile(path.."/aliases.lua") -- Aliases for compability diff --git a/mods/mtg_plus/ladders.lua b/mods/mtg_plus/ladders.lua new file mode 100644 index 00000000..020e8447 --- /dev/null +++ b/mods/mtg_plus/ladders.lua @@ -0,0 +1,125 @@ +local S = minetest.get_translator("mtg_plus") + +local metal_sounds, wood_sounds +if default.node_sound_metal_defaults then + metal_sounds = default.node_sound_metal_defaults() +else + metal_sounds = default.node_sound_stone_defaults() +end +wood_sounds = default.node_sound_wood_defaults() + +-- Ladders +minetest.register_node("mtg_plus:ladder_papyrus", { + description = S("Papyrus Ladder"), + _doc_items_longdesc = S("A particulary strong piece of ladder which allows you to move vertically."), + drawtype = "signlike", + tiles = {"mtg_plus_ladder_papyrus.png"}, + inventory_image = "mtg_plus_ladder_papyrus.png", + wield_image = "mtg_plus_ladder_papyrus.png", + paramtype = "light", + paramtype2 = "wallmounted", + sunlight_propagates = true, + walkable = false, + climbable = true, + is_ground_content = false, + selection_box = { type = "wallmounted", }, + groups = { snappy = 2, choppy = 1, flammable = 2 }, + sounds = default.node_sound_leaves_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:ladder_papyrus 2", + recipe = { {"default:papyrus", "", "default:papyrus"}, + {"farming:string", "default:papyrus", "farming:string"}, + {"default:papyrus", "", "default:papyrus"}}, +}) + +minetest.register_craft({ + type = "fuel", + recipe = "mtg_plus:ladder_papyrus", + burntime = 2, +}) + +local simple_ladders = { + { "gold", "metal", S("Golden Ladder"), "default:gold_ingot", { cracky = 3 }, true }, + { "bronze", "metal", S("Bronze Ladder"), "default:bronze_ingot", { cracky = 2 } }, + { "copper", "metal", S("Copper Ladder"), "default:copper_ingot", { cracky = 2 } }, + { "tin", "metal", S("Tin Ladder"), "default:tin_ingot", { cracky = 2 } }, + { "aspen_wood", "wood", S("Aspen Wood Ladder"), "default:aspen_wood", { choppy = 3, flammable = 1 }, nil, 5 }, + { "acacia_wood", "wood", S("Acacia Wood Ladder"), "default:acacia_wood", { choppy = 3, flammable = 1 }, nil, 8 }, + { "pine_wood", "wood", S("Pine Wood Ladder"), "default:pine_wood", { choppy = 3, flammable = 1 }, nil, 6 }, + { "junglewood", "wood", S("Jungle Wood Ladder"), "default:junglewood", { choppy = 3, flammable = 1 }, nil, 9 }, + { "goldwood", "wood", S("Goldwood Ladder"), "mtg_plus:goldwood", { choppy = 2 }, true }, +} + +for m=1, #simple_ladders do + local ladder = simple_ladders[m] + local longdesc + if ladder[6] then + longdesc = S("A luxurious piece of ladder which allows you to move vertically.") + else + longdesc = S("A piece of ladder which allows you to move vertically.") + end + if ladder[2] == "metal" then + sounds = metal_sounds + else + sounds = wood_sounds + end + minetest.register_node("mtg_plus:ladder_"..ladder[1], { + description = ladder[3], + _doc_items_longdesc = longdesc, + drawtype = "signlike", + tiles = {"mtg_plus_ladder_"..ladder[1]..".png"}, + inventory_image = "mtg_plus_ladder_"..ladder[1]..".png", + wield_image = "mtg_plus_ladder_"..ladder[1]..".png", + paramtype = "light", + paramtype2 = "wallmounted", + sunlight_propagates = true, + walkable = false, + climbable = true, + is_ground_content = false, + selection_box = { type = "wallmounted", }, + groups = ladder[5], + sounds = sounds, + }) + if ladder[2] == "metal" then + minetest.register_craft({ + output = "mtg_plus:ladder_"..ladder[1].." 15", + recipe = { + {ladder[4], "", ladder[4]}, + {ladder[4], ladder[4], ladder[4]}, + {ladder[4], "", ladder[4]}, + }, + }) + else + minetest.register_craft({ + output = "mtg_plus:ladder_"..ladder[1].." 9", + recipe = { + {"group:stick", "", "group:stick"}, + {"group:stick", ladder[4], "group:stick"}, + {"group:stick", "", "group:stick"}, + }, + }) + if ladder[7] ~= nil then + minetest.register_craft({ + type = "fuel", + recipe = "mtg_plus:ladder_"..ladder[1], + burntime = ladder[7], + }) + end + end +end + +-- Tweak the default ladder +minetest.override_item("default:ladder_wood", { description = S("Apple Wood Ladder") }) +minetest.register_craft({ + output = "default:ladder_wood 9", + recipe = { + {"group:stick", "", "group:stick"}, + {"group:stick", "default:wood", "group:stick"}, + {"group:stick", "", "group:stick"}, + }, +}) + +-- The default stick-only recipe for default ladder will be intentionally kept for +-- convenience. diff --git a/mods/mtg_plus/locale/mtg_plus.de.tr b/mods/mtg_plus/locale/mtg_plus.de.tr new file mode 100644 index 00000000..7abf4965 --- /dev/null +++ b/mods/mtg_plus/locale/mtg_plus.de.tr @@ -0,0 +1,167 @@ +# textdomain:mtg_plus +Aggregated Diamond Block=Aggregieter Diamantblock +Bronze Brick=Bronzeziegel +Bronze Brick Slab=Bronzeziegelplatte +Bronze Brick Stair=Bronzeziegeltreppe +Copper Brick=Kupferziegel +Copper Brick Slab=Kupferziegelplatte +Copper Brick Stair=Kupferziegeltreppe +Dense Ice Tile=Kompakteiskachel +Desert Stone Brick with Golden Edges=Wüstensteinziegel mit Goldkanten +Diamond Block with Golden Frame=Diamantblock mit Goldrahmen +Dirty Glass=Schmutziges Glas +Soft Dirt Brick=Weicher Erdziegel +Hardened Dirt Brick=Gehärteter Erdziegel +Hardened Dirt Brick Slab=Gehärtete Erdziegelplatte +Hardened Dirt Brick Stair=Gehärtete Erdziegeltreppe +Cobbled Gravel=Kiespflaster +Cobbled Gravel Slab=Kiespflasterplatte +Cobbled Gravel Stair=Kiespflastertreppe +Cobbled Gravel Wall=Kiespflastermauer +Gravel Dirt=Kieserde +Gold Brick=Goldziegel +Gold Brick Slab=Goldziegelplatte +Gold Brick Stair=Goldziegeltreppe +Golden Ladder=Goldleiter +Copper Ladder=Kupferleiter +Bronze Ladder=Bronzeleiter +Tin Ladder=Zinnleiter +Golden Window=Goldfenster +Goldglass=Goldglas +Goldglass Pane=Goldglasscheibe +Goldwood=Goldholz +Goldwood Fence=Goldholzzaun +Goldwood Fence Gate=Goldholzzauntor +Goldwood Fence Rail=Goldholzzaungeländer +Goldwood Slab=Goldholzplatte +Goldwood Stair=Goldholztreppe +Hard Snow Brick=Hartschneeziegel +Hard Snow Brick Slab=Hartschneeziegelplatte +Hard Snow Brick Stair=Hartschneeziegeltreppe +Ice Block=Eisblock +Ice Block Slab=Eisblockplatte +Ice Block Stair=Eisblocktreppe +Ice Brick=Eisziegel +Ice Brick Slab=Eisziegelplatte +Ice Brick Stair=Eisziegeltreppe +Ice Door=Eistür +Ice Trapdoor=Eisfalltür +Ice Tile=Eiskachel +Ice Tile Slab=Eiskachelplatte +Ice Tile Stair=Eiskacheltreppe +Ice Window=Eisfenster +Ice Window Pane=Eisfensterscheibe +Icy Snow Brick=Eisschneeziegel +Icy Snow Brick Slab=Eisschneeziegelplatte +Icy Snow Brick Stair=Eisschneeziegeltreppe +Icy Steel Door=Eisstahltür +Icy Steel Trapdoor=Eisstahlfalltür +Jungle Cobblestone=Dschungelkopfsteinpflaster +Jungle Cobblestone Slab=Dschungelkopfsteinpflasterplatte +Jungle Cobblestone Stair=Dschungelkopfsteinpflastertreppe +Obsidian Brick with Golden Edges=Obsidianziegel mit Goldkanten +Paper Barrier=Papierbarriere +Papyrus Block=Papyrusblock +Papyrus Door=Papyrustür +Papyrus Ladder=Papyrusleiter +Papyrus Lattice=Papyrusgitter +Sandstone Brick with Golden Edges=Sandsteinziegel mit Goldkanten +Cobbled Sandstone=Sandsteinpflaster +Cobbled Sandstone Slab=Sandsteinpflasterplatte +Cobbled Sandstone Stair=Sandsteinpflastertreppe +Soft Snow Brick=Weichschneeziegel +Steel Brick=Stahlziegel +Steel Brick Slab=Stahlziegelplatte +Steel Brick Stair=Stahlziegeltreppe +Stone Brick with Golden Edges=Steinziegel mit Goldkanten +Wooden Bar Door=Holzgittertür +Wooden Bars=Holzgitter +Jungle Cobblestone Wall=Dschungelkopfsteinpflastermauer +Dense Ice Tile Wall=Kompakteiskachelmauer +Cobbled Sandstone Wall=Sandsteinpflastermauer +Goldwood Chair=Goldholzstuhl +Goldwood Stool=Goldholzhocker +Goldwood Table=Goldholztisch +Jungle Cobblestone Stool=Dschungelkopfsteinpflasterhocker +Jungle Cobblestone Table=Dschungelkopfsteinpflastertisch +Cobbled Sandstone Stool=Sandsteinpflasterhocker +Cobbled Sandstone Table=Sandsteinpflastertisch +Cobbled Gravel Stool=Kiespflasterhocker +Cobbled Gravel Table=Kiespflastertisch +Flint Block=Feuersteinblock +Flint Block Stair=Feuersteinblocktreppe +Flint Block Slab=Feuersteinblockplatte +Desert Sandstone Brick with Golden Edges=Wüstensandsteinziegel mit Goldkanten +Silver Sandstone Brick with Golden Edges=Silbersandsteinziegel mit Goldkanten +Tin Brick=Zinnziegel +Tin Brick Slab=Zinnziegelplatte +Tin Brick Stair=Zinnziegeltreppe +Cobbled Silver Sandstone=Silbersandsteinpflaster +Cobbled Silver Sandstone Slab=Silbersandsteinpflasterplatte +Cobbled Silver Sandstone Stair=Silbersandsteinpflastertreppe +Cobbled Silver Sandstone Wall=Silbersandsteinpflastermauer +Cobbled Desert Sandstone=Wüstensandsteinpflaster +Cobbled Desert Sandstone Slab=Wüstensandsteinpflasterplatte +Cobbled Desert Sandstone Stair=Wüstensandsteinpflastertreppe +Cobbled Desert Sandstone Wall=Wüstensandsteinpflastermauer +Cobbled Silver Sandstone Stool=Silbersandsteinpflasterhocker +Cobbled Silver Sandstone Table=Silbersandsteinpflastertisch +Cobbled Desert Sandstone Stool=Wüstensandsteinpflasterhocker +Cobbled Desert Sandstone Table=Wüstensandsteinpflastertisch +Soft Snow Brick Slab=Weichschneeziegelplatte +Soft Snow Brick Stair=Weichschneeziegeltreppe +Historic City=Historische Stadt +Place a aggregated diamond block.=Platzieren Sie einen aggregierten Diamantblock. +Ice doors can be opened and closed. They are solid, but some of the light hitting ice doors can still go through, making them an interesting decoration in icy areas.=Eistüren können geöffnet und geschlossen werden. Sie sind solide, aber etwas des Lichts, welches auf Eistüren einfällt, gelangt hindurch, was sie zu einer interessanten Dekoration in eiskalten Gebieten macht. +A luxurious piece of ladder which allows you to move vertically.=Ein luxoriöses Leitersegment, auf dem Sie sich vertikal bewegen können. +A piece of ladder which allows you to move vertically.=Ein Leitersegment, auf dem Sie sich vertikal bewegen können. +Green Jungle=Grüner Dschungel +This is a fence made out of precious goldwood. The fence will neatly connect to its neighbors, making it easy to build nice-looking fence structures. The fence can be jumped over.=Dies ist ein Zaun, angefertigt aus dem wertvollen Goldholz. Der Zaun verbindet sich praktischerweise mit seinen Nachbarn; damit kann man leicht lange Zäune errichten. Man kann über den Zaun springen. +This is a fence rail made out of precious goldwood. It will neatly connect to its neighbors, but without creating fence posts. It can be jumped over.=Dies ist ein Zaungeländer, angefertigt aus dem wertvollen Goldholz. Er verbindet sich praktischerweise mit seinen Nachbarn, aber ohne dabei Zaunpfosten zu erzeugen. Man kann über das Geländer springen. +Goldwood is a precious artificial kind of wood made by enriching wood with gold. Goldwood is fireproof and notable for its bright yellowy appearance.=Goldholz ist eine wertvolle künstliches Art Holz, welches mit Gold angereichert wurde. Goldholz ist feuersicher und fällt durch seine helle gelbliche Farbe auf. +Ice window panes are thinner than the full ice windows and neatly connect to each other as you build them=Eisfensterscheiben sind dünner als die vollen Eisfenster und verbinden sich praktischerweise zu den anderen Fensterscheiben beim Bauen. +Craft 100 goldwood.=Fertigen Sie 100 Goldholz. +Icy steel doors are a combination of ice doors and steel doors which can only be opened and closed by their owners. They are solid, but some of the light hitting icy steel doors can still go through.=Eisstahltüren sind eine Verbindung von Eistüren und Stahltüren, welche nur von ihrem Eigentümer geöffnet werden können. Sie sind solide, aber etwas Licht scheint durch Eisstahltüren hindurch. +A ornamental and mostly transparent block, made by combining glass with gold.=Ein größtenteils durchsichtiger Zierblock, der durch die Kombinierung von Glas mit Gold entstanden ist. +This decorational ice tile has been crafted in a way that it is partially transparent and looks like a real window.=Diese dekorative Eiskachel wurde so gefertigt, dass sie teilweise durchsichtig ist und wie ein echtes Fenster aussieht. +Small Gold-framed Diamond Block=Kleiner mit Gold umrahmter Diamantblock +Luxurious Adornment=Luxuriöse Verzierung +Craft 4 small gold-framed diamond blocks.=Fertigen sie 4 kleine mit Gold umrahmte Diamantblöcke an. +Papyrus Panic=Papyruspanik +An ice trapdoor covers the floor and can be opened and closed by anyone. Ice trapdoors are solid, but some light can pass through nonetheless.=Eine Eisfalltür bedeckt den Boden und kann von jedem geöffnet und geschlossen werden. Eisfalltüren sind solide, aber etwas Licht scheint durch. +Rich Carpenter=Die Luxus-Zimmerei +Let's build an igloo!=Lasst uns ein Iglu bauen! +Yellow Desert=Gelbe Wüste +Cobbled gravel is solidified gravel, carefully arranged in a mosaic-like pattern. It makes a nice building material.=Kiespflaster ist ein verfestigte Form von Kies, welches behutsam in ein eine Art Mosaik angeordnet wurde. +Gravel dirt is a type of dirt consisting of equal parts of gravel and dirt. It combines some of the properties of gravel and dirt.=Kieserde ist eine Art Erde, welche zu gleichen Teilen aus Kies und Erde besteht. Sie kombiniert ein paar der Eigenschaften von Kies und Erde. +Build 100 papyrus blocks.=Fertigen Sie 100 Papyrusblöcke. +This block is even harder than diamond; diamond pickaxes can't break it. TNT is able to destroy this block.=Dieser Block ist sogar härter als Diamant; Diamantspitzhacken können ihn nicht zerbrechen. TNT kann diesen Block zerstören. +Craft 400 cobbled gravel.=Fertigen Sie 400 Kiespflaster. +A particulary strong piece of ladder which allows you to move vertically.=Ein besonders robustes Leitersegment, auf dem Sie sich vertikal bewegen können. +Self-proclaimed Winner=Selbsternannter Sieger +Place 225 hard snow bricks.=Fertigen Sie 225 Hartschneeziegel. +Craft 512 jungle cobblestone.=Fertigen Sie 512 Dschungelkopfsteinpflaster. +Ice Crazy=Verrückt nach Eis +Craft 128 dense ice tiles.=Fertigen Sie 128 Kompakteiskacheln +A fence gate made from precious goldwood. It blocks the path, but it can be opened and easily jumped over. Other fence posts will neatly connect to this fence gate.=Ein Zauntor aus wertvollem Goldholz. Er blockiert den Durchgang, aber er kann geöffnet werden und man kann einfach rüberspringen. Andere Zaunpfosten werden sich automatisch mit diesem Zauntor verbinden. +Can't dig me!=Ich bin unzerbrechlich! +Craft a golden cup.=Fertigen Sie einen Goldpokal. +Craft 512 cobbled sandstone.=Fertigen Sie 512 Sandsteinpflaster. +An icy steel trapdoor is a combination of an ice trapdoor and a steel trapdoor. It covers the floor and can only be opened and closed by its placer. Icy steel trapdoors are solid, but some light can pass through nonetheless.=Eine Eisstahlfalltür ist eine Kombination aus Eisfalltür und Stahlfalltür. Sie bedeckt den Boden und kann nur vom Eigentümer geöffnet oder geschlossen werden. Eisstahlfalltüren sind solide, aber etwas Licht scheint hindurch. +A decorative, semitransparent block. The dirt makes it hard for the sunlight to pass through.=Ein dekorativer halbtransparenter Block. Der Dreck reduziert die Lichtstärke. +Papier barriers are thin solid layers of paper which neatly connect to their neighbors as you build them. They could be useful to separate rooms.=Papierbarrieren sind dünne feste Papierschichten, die sich mit ihren Nachbarblöcken verbinden. Sie könnten nützlich sein, um Räume zu teilen. +Wooden bars are barriers which neatly connect to their neighbors as you build them.=Holzgitter sind Barrieren, die sich mit ihren Nachbarblöcken verbinden. +Goldglass panes are thin layers of goldglass which neatly connect to their neighbors as you build them.=Goldglasscheiben sind dünne Goldglasschichten, die sich mit ihren Nachbarblöcken verbinden. +Golden windows are decorative blocks which can be placed into holes for nice-looking windows. Golden windows automatically connect to their neighbors as you build them.=Goldfenster sind dünne dekorative Blöcke, die in Lücken als gutaussehende Fenster platziert werden können. Goldfenster verbinden sich automatisch mit ihren Nachbarn. +Papyrus lattices are strong barriers which neatly connect to their neighbors as you build them.=Papyrusgitter sind stabile Barrieren, die sich automatisch mit ihren Nachbarn verbinden. +A door covers a vertical area of two blocks to block the way. It can be opened and closed by any player.=Eine Tür belegt einen vertikalen Bereich von zwei Blöcken, um den Weg zu blockieren. Sie kann von jedem Spieler geöffnet und geschlossen werden. +Use the use key on it to open or close it.=Benutzen Sie die „Benutzen“-Taste auf ihr, um sie zu öffnen oder zu schließen. +Point the icy steel trapdoor to see who owns it. Use the use key on it to open or close it (if you own it).=Zeigen Sie auf die Eisstahlfalltür, um zu sehen, wem sie gehört. Benutzen Sie die „Benutzen“-Taste auf ihr, um sie zu öffnen oder zu schließen (wenn sie Ihnen gehört). +Point the door to see who owns it. Use the use key on the door to open or close it (if you own it).=Zeigen Sie auf die Tür, um zu sehen, wem sie gehört. Benutzen Sie die „Benutzen“-Taste auf ihr, um sie zu öffnen oder zu schließen (wenn sie Ihnen gehört). +Apple Wood Ladder=Apfelholzleiter +Acacia Wood Ladder=Akazienholzleiter +Pine Wood Ladder=Pinienholzleiter +Aspen Wood Ladder=Espenholzleiter +Jungle Wood Ladder=Dschungelholzleiter +Goldwood Ladder=Goldholzleiter +A piece of wall creates simple barriers that connect to neighbor blocks. Walls can be jumped over.=Ein Stück Mauer erzeugt einfache Barrieren, welche sich mit den Nachbarblöcken verbinden. Man kann über Mauern springen. diff --git a/mods/mtg_plus/locale/mtg_plus.fr.tr b/mods/mtg_plus/locale/mtg_plus.fr.tr new file mode 100644 index 00000000..439bea8b --- /dev/null +++ b/mods/mtg_plus/locale/mtg_plus.fr.tr @@ -0,0 +1,245 @@ +# textdomain: mtg_plus + + +### awards.lua ### + +Build 100 papyrus blocks.=Contruit 100 bloc de papyrus. +Can't dig me!=Indestructible ! +Craft 100 goldwood.=Assemble 100 morceau de bois doré. +Craft 128 dense ice tiles.=Assemble 128 tuile de glace condensée. +Craft 4 small gold-framed diamond blocks.=Assemble 4 quatre petits blocs de diamant encadrés d'or. +Craft 400 cobbled gravel.=Assemble 400 pavés de gravier. +Craft 512 cobbled sandstone.=Assemble 512 pavés de grès. +Craft 512 jungle cobblestone.=Assemble 512 pavés de la jungle. +Green Jungle=Jungle pavée +Historic City=Ville historique +Ice Crazy=Fou de glace +Let's build an igloo!=Batissons un igloo! +Luxurious Adornment=Luxe ornemental +Papyrus Panic=Panique au papyrus +Place 225 hard snow bricks.=Mettre 225 briques de neige compactées. +Place a aggregated diamond block.=Mettre un agrégat de diamant. +Rich Carpenter=Carpentier riche +Yellow Desert=Désert pavé + +### brickblocks.lua ### + +Bronze Brick=Brique de bronze +Copper Brick=Brique de cuivre +Dense Ice Tile=Tuile de glace condensée +Desert Sandstone Brick with Golden Edges=Brique de grès du désert encadrée d'or +Desert Stone Brick with Golden Edges=Roche du désert encadrée d'or +Flint Block=Bloc de silex +Gold Brick=Brique d'or +Hard Snow Brick=Brique de neige compactée +Hardened Dirt Brick=Brique de terre durcie +Ice Block=Bloc de glace +Ice Brick=Brique de glace +Ice Tile=Tuile de glace +Icy Snow Brick=Brique de neige glacée +Obsidian Brick with Golden Edges=Brique d'obsidienne encadrée d'or +Papyrus Block=Bloc de papyrus +Sandstone Brick with Golden Edges=Brique de grès encadrée d'or +Silver Sandstone Brick with Golden Edges=Brique de grès argenté encadrée d'or +Small Gold-framed Diamond Block=Petit bloc de diamant encadré d'or +Soft Dirt Brick=Brique de terre +Soft Snow Brick=Brique de neige +Steel Brick=Brique d'acier +Stone Brick with Golden Edges=Brique de pierre encadrée d'or +Tin Brick=Brique d'étain + +### cobble.lua ### + +Cobbled gravel is solidified gravel, carefully arranged in a mosaic-like pattern. It makes a nice building material.= + + +### cobble.lua ### +### extras.lua ### + +Cobbled Desert Sandstone=Pavé de grès du désert +Cobbled Gravel=Pavé de gravier +Cobbled Sandstone=Pavé de grès +Cobbled Silver Sandstone=Pavé de grès argenté +Jungle Cobblestone=Pavé de la jungle + +### doors.lua ### + +A door covers a vertical area of two blocks to block the way. It can be opened and closed by any player.=Une porte obstrue deux blocs d'espace vertical. Elle peut être ouverte et fermée par n'importe quel joueur. + +A fence gate made from precious goldwood. It blocks the path, but it can be opened and easily jumped over. Other fence posts will neatly connect to this fence gate.=Un portail de bois doré précieux. Il bloque le passage mais peut être ouvert et sauté par-dessus facilement. Les autres poteaux de clôture se joignent parfaitement à ce portail. + +An ice trapdoor covers the floor and can be opened and closed by anyone. Ice trapdoors are solid, but some light can pass through nonetheless.=Une trappe de glace bouche un trou de plancher et peut être ouverte ou fermé par n'importe qui. La trappe solide mais translucide. + +An icy steel trapdoor is a combination of an ice trapdoor and a steel trapdoor. It covers the floor and can only be opened and closed by its placer. Icy steel trapdoors are solid, but some light can pass through nonetheless.=Une trappe d'acier gelé est une combinaison d'une trappe de glace et d'une d'acier. Elle bouche un trou de plancher et ne peut être ouverte et fermée que par son propriétaire. La trappe solide mais translucide. + +Goldwood Fence Gate=Portail de bois doré +Ice Door=Porte de glace +Ice Trapdoor=Trappe de glace + +Ice doors can be opened and closed. They are solid, but some of the light hitting ice doors can still go through, making them an interesting decoration in icy areas.=Les portes de glaces peuvent être ouvertes et fermeés. Elles laissent passer la lumière, et donc forme un élément de décoration intéressant dans les endroits glacés. + +Icy Steel Door=Porte d'acier gelée +Icy Steel Trapdoor=Trappe d'acier gelée + +Icy steel doors are a combination of ice doors and steel doors which can only be opened and closed by their owners. They are solid, but some of the light hitting icy steel doors can still go through.=Les portes d'acier gelée combinent une porte d'acier et une porte de glace, lesquelles ne peuvent être ouvertes ou fermeés par leur propriétaire. Elles sont solides mais translucides. + +Papyrus Door=Porte de papyrus + +Point the door to see who owns it. Use the use key on the door to open or close it (if you own it).=Pointez sur la porte pour en connaître le propriétaire. Utilisez la clef sur la porte pour l'ouvrir ou fermer (si vous l'avez). + +Point the icy steel trapdoor to see who owns it. Use the use key on it to open or close it (if you own it).=Pointez sur la trappe d'acier gelé pour en connaître le propriétaire. Utilisez la clef sur la porte pour l'ouvrir ou fermer (si vous l'avez). + +Right-click the gate to open or close it.=Clic droit sur le portillon pour l'ouvrir ou le fermer. +Use the use key on it to open or close it.=Utilisez la clef dessus pour l'ouvrir ou la fermer. +Wooden Bar Door=Porte de barreaux de bois + +### extras.lua ### + +Cobbled Desert Sandstone Stool=Tabouret en pavé de grès du désert +Cobbled Desert Sandstone Table=Table en pavé de grès du désert +Cobbled Gravel Stool=Tabouret en pavé de gravier +Cobbled Gravel Table=Table en pavé de gravier +Cobbled Sandstone Stool=Tabouret en pavé de grès +Cobbled Sandstone Table=Table en pavé de grès +Cobbled Silver Sandstone Stool=Tabouret en pavé de grès argenté +Cobbled Silver Sandstone Table=Table en pavé de grès argenté +Goldwood Chair=Chaise de bois doré +Goldwood Stool=Tabouret de bois doré +Goldwood Table=Table de bois doré +Jungle Cobblestone Stool=Tabouret en pavé de la jungle +Jungle Cobblestone Table=Table en pavé de la jungle + +### extras.lua ### +### specials.lua ### + +Goldwood=Bois doré + +### ladders.lua ### + +A luxurious piece of ladder which allows you to move vertically.= + +A particulary strong piece of ladder which allows you to move vertically.= + +A piece of ladder which allows you to move vertically.=Un morceau d'échelle qui permet de grimper. +Acacia Wood Ladder=Échelle d'acacia +Apple Wood Ladder=Échelle de pommier +Aspen Wood Ladder=Échelle de tremble +Bronze Ladder=Échelle de bronze +Copper Ladder=Échelle de cuivre +Golden Ladder=Échelle d'or +Goldwood Ladder=Échelle de bois doré +Jungle Wood Ladder=Échelle de bois de la jungle +Papyrus Ladder=Échelle de papyrus +Pine Wood Ladder=Échelle de pin +Tin Ladder=Échelle d'étain + +### seethrough.lua ### + +# textdomain:mtg_plus +A decorative, semitransparent block. The dirt makes it hard for the sunlight to pass through.= + +A ornamental and mostly transparent block, made by combining glass with gold.=Un bloc ornemental transparent, assemblé de verre et d'or. + +Dirty Glass=Verre sale +Goldglass=Verre doré +Ice Window=Fenêtre de glace + +This decorational ice tile has been crafted in a way that it is partially transparent and looks like a real window.=Cette pièce de glace décorative a été confectionnée pour ressemblé à une vrai fenêtre. + + +### specials.lua ### + +Aggregated Diamond Block=Agrégat de diamant + +Goldwood is a precious artificial kind of wood made by enriching wood with gold. Goldwood is fireproof and notable for its bright yellowy appearance.=Le bois doré est un bois artificel précieux créé en injectant de l'or. Le bois doré est ininflammable et remarquable de son apparence jaune brillant. + +Gravel Dirt=Gravier terreux + +Gravel dirt is a type of dirt consisting of equal parts of gravel and dirt. It combines some of the properties of gravel and dirt.=Le gravier terreux est un mélange à parts égales de gravier et de terre. Il combine certaines propriétés des deux. + +This block is even harder than diamond; diamond pickaxes can't break it. TNT is able to destroy this block.=Ce bloc est encore plus endurci que le diamant ; même les pioches de diamant ne peuvent le faire craquer. Seul le TNT peut le détruire. + + +### stairslabs.lua ### + +Bronze Brick Slab=Dalle en brique de bronze +Bronze Brick Stair=Marche en brique de bronze +Cobbled Desert Sandstone Slab=Dalle en pavé de grès du désert +Cobbled Desert Sandstone Stair=Marche en pavé de grès du désert +Cobbled Gravel Slab=Dalle en pavé de gravier +Cobbled Gravel Stair=Marche en pavé de gravier +Cobbled Sandstone Slab=Dalle en pavé de grès +Cobbled Sandstone Stair=Marche en pavé de grès +Cobbled Silver Sandstone Slab=Dalle en pavé de grès argenté +Cobbled Silver Sandstone Stair=Marche en pavé de grès argenté +Copper Brick Slab=Dalle en brique de cuivre +Copper Brick Stair=Marche en brique de cuivre +Flint Block Slab=Dalle en silex +Flint Block Stair=Marche en silex +Gold Brick Slab=Dalle en brique d'or +Gold Brick Stair=Marche en brique d'or +Goldwood Slab=Dalle en bois doré +Goldwood Stair=Marche en bois doré +Hard Snow Brick Slab=Dalle en brique de neige compactée +Hard Snow Brick Stair=Marche en brique de neige compactée +Hardened Dirt Brick Slab=Dalle en terre durcie +Hardened Dirt Brick Stair=Marche en terre durcie +Ice Block Slab=Dalle de glace +Ice Block Stair=Marche de glace +Ice Brick Slab=Dalle en brique de glace +Ice Brick Stair=Marche en brique de glace +Ice Tile Slab=Dalle en tuile de glace +Ice Tile Stair=Marche en tuile de glace +Icy Snow Brick Slab=Dalle en brique de neige glacée +Icy Snow Brick Stair=Marche en brique de neige glacée +Jungle Cobblestone Slab=Dalle en pavé de la jungle +Jungle Cobblestone Stair=Marche en pavé de la jungle +Soft Snow Brick Slab=Dalle en brique de neige +Soft Snow Brick Stair=Marche en brique de neige +Steel Brick Slab=Dalle en brique d'acier +Steel Brick Stair=Marche en brique d'acier +Tin Brick Slab=Dalle en brique d'étain +Tin Brick Stair=Marche en brique d'étain + +### wallfences.lua ### + +A piece of wall creates simple barriers that connect to neighbor blocks. Walls can be jumped over.=Un morceau de mur sert de simple barrière qui s'agence aux blocs voisins. Vous pouvez sauter par dessus les murs. + +Cobbled Desert Sandstone Wall=Mur en pavé de grès du désert +Cobbled Gravel Wall=Mur en pavé de gravier +Cobbled Sandstone Wall=Mur en pavé de grès +Cobbled Silver Sandstone Wall=Mur en pavé de grès argenté +Dense Ice Tile Wall=Mur en tuile de glace condensée +Goldwood Fence=Barrière de bois doré +Goldwood Fence Rail=Clôture de bois doré +Jungle Cobblestone Wall=Mur en pavé de la jungle + +This is a fence made out of precious goldwood. The fence will neatly connect to its neighbors, making it easy to build nice-looking fence structures. The fence can be jumped over.=Cette barrière est faite de bois doré précieux. Elle s'agence proprement aux blocs voisins, ce qui rend la construction de belle clôture simple. Il est possible de sauter par dessus. + +This is a fence rail made out of precious goldwood. It will neatly connect to its neighbors, but without creating fence posts. It can be jumped over.=Cette clôture est faite de bois doré précieux. Elle s'agence proprement aux blocs voisins, ce qui rend la construction de belle clôture simple. Il est possible de sauter par dessus. + +### xpanes.lua ### + +Golden Window=Fenêtre dorée + +Golden windows are decorative blocks which can be placed into holes for nice-looking windows. Golden windows automatically connect to their neighbors as you build them.=Les fenêtres dorées sont des blocs décoratifs qui penvent être placé dans des trous pour obtenir de jolies fenêtres. Elles s'agencent automatiquement avec leurs nœuds avoisiants durant la construction. + +Goldglass Pane=Panneau de verre doré + +Goldglass panes are thin layers of goldglass which neatly connect to their neighbors as you build them.=Les panneaux de verre dorés sont de mince couche de verre doré qui s'agencent bien avec leurs nœuds avoisiants durant la construction. + +Ice Window Pane=Panneau en verre de glace + +Ice window panes are thinner than the full ice windows and neatly connect to each other as you build them=Les panneaux en verre de glace sont plus mince que les fenêtres complète et s'agencent bien avec leurs nœuds avoisiants durant la construction. + +Paper Barrier=Barrière de papier + +Papier barriers are thin solid layers of paper which neatly connect to their neighbors as you build them. They could be useful to separate rooms.=Les barrières de papier sont de minces couches solides qui s'agencent bien avec leurs nœuds avoisiants durant la construction. Elles penvent être utiles pour séparer des pièces dans une bâtisse. + +Papyrus Lattice=Trellis de papyrus + +Papyrus lattices are strong barriers which neatly connect to their neighbors as you build them.=Les trellis de papyrus sont forment des barrières solides et s'agencent bien avec leur nœuds avoisiants durant la construction. + +Wooden Bars=Barreaux de bois. + +Wooden bars are barriers which neatly connect to their neighbors as you build them.=Les barreaux de bois forment une cloison qui s'agencent bien avec leurs nœuds avoisiants durant la construction. diff --git a/mods/mtg_plus/locale/template.txt b/mods/mtg_plus/locale/template.txt new file mode 100644 index 00000000..eb893122 --- /dev/null +++ b/mods/mtg_plus/locale/template.txt @@ -0,0 +1,166 @@ +# textdomain:mtg_plus +A decorative, semitransparent block. The dirt makes it hard for the sunlight to pass through.= +A fence gate made from precious goldwood. It blocks the path, but it can be opened and easily jumped over. Other fence posts will neatly connect to this fence gate.= +Aggregated Diamond Block= +A luxurious piece of ladder which allows you to move vertically.= +A piece of ladder which allows you to move vertically.= +An ice trapdoor covers the floor and can be opened and closed by anyone. Ice trapdoors are solid, but some light can pass through nonetheless.= +An icy steel trapdoor is a combination of an ice trapdoor and a steel trapdoor. It covers the floor and can only be opened and closed by its placer. Icy steel trapdoors are solid, but some light can pass through nonetheless.= +A ornamental and mostly transparent block, made by combining glass with gold.= +A particulary strong piece of ladder which allows you to move vertically.= +Bronze Brick= +Bronze Brick Slab= +Bronze Brick Stair= +Build 100 papyrus blocks.= +Can't dig me!= +Cobbled Desert Sandstone= +Cobbled Desert Sandstone Slab= +Cobbled Desert Sandstone Stair= +Cobbled Desert Sandstone Stool= +Cobbled Desert Sandstone Table= +Cobbled Desert Sandstone Wall= +Cobbled Gravel= +Cobbled gravel is solidified gravel, carefully arranged in a mosaic-like pattern. It makes a nice building material.= +Cobbled Gravel Slab= +Cobbled Gravel Stair= +Cobbled Gravel Stool= +Cobbled Gravel Table= +Cobbled Gravel Wall= +Cobbled Sandstone= +Cobbled Sandstone Slab= +Cobbled Sandstone Stair= +Cobbled Sandstone Stool= +Cobbled Sandstone Table= +Cobbled Sandstone Wall= +Cobbled Silver Sandstone= +Cobbled Silver Sandstone Slab= +Cobbled Silver Sandstone Stair= +Cobbled Silver Sandstone Stool= +Cobbled Silver Sandstone Table= +Cobbled Silver Sandstone Wall= +Copper Brick= +Copper Brick Slab= +Copper Brick Stair= +Craft 100 goldwood.= +Craft 128 dense ice tiles.= +Craft 400 cobbled gravel.= +Craft 4 small gold-framed diamond blocks.= +Craft 512 cobbled sandstone.= +Craft 512 jungle cobblestone.= +Craft a golden cup.= +Dense Ice Tile= +Dense Ice Tile Wall= +Desert Sandstone Brick with Golden Edges= +Desert Stone Brick with Golden Edges= +Dirty Glass= +Flint Block= +Flint Block Slab= +Flint Block Stair= +Gold Brick= +Gold Brick Slab= +Gold Brick Stair= +Golden Ladder= +Copper Ladder= +Tin Ladder= +Bronze Ladder= +Golden Window= +Goldglass= +Goldglass Pane= +Goldwood= +Goldwood Chair= +Goldwood Fence= +Goldwood Fence Gate= +Goldwood Fence Rail= +Goldwood is a precious artificial kind of wood made by enriching wood with gold. Goldwood is fireproof and notable for its bright yellowy appearance.= +Goldwood Slab= +Goldwood Stair= +Goldwood Stool= +Goldwood Table= +Gravel Dirt= +Gravel dirt is a type of dirt consisting of equal parts of gravel and dirt. It combines some of the properties of gravel and dirt.= +Green Jungle= +Hardened Dirt Brick= +Hardened Dirt Brick Slab= +Hardened Dirt Brick Stair= +Hard Snow Brick= +Hard Snow Brick Slab= +Hard Snow Brick Stair= +Historic City= +Ice Block= +Ice Block Slab= +Ice Block Stair= +Ice Brick= +Ice Brick Slab= +Ice Brick Stair= +Ice Crazy= +Ice Door= +Ice doors can be opened and closed. They are solid, but some of the light hitting ice doors can still go through, making them an interesting decoration in icy areas.= +Ice Tile= +Ice Tile Slab= +Ice Tile Stair= +Ice Trapdoor= +Ice Window= +Ice Window Pane= +Ice window panes are thinner than the full ice windows and neatly connect to each other as you build them= +Icy Snow Brick= +Icy Snow Brick Slab= +Icy Snow Brick Stair= +Icy Steel Door= +Icy steel doors are a combination of ice doors and steel doors which can only be opened and closed by their owners. They are solid, but some of the light hitting icy steel doors can still go through.= +Icy Steel Trapdoor= +Jungle Cobblestone= +Jungle Cobblestone Slab= +Jungle Cobblestone Stair= +Jungle Cobblestone Stool= +Jungle Cobblestone Table= +Jungle Cobblestone Wall= +Let's build an igloo!= +Luxurious Adornment= +Obsidian Brick with Golden Edges= +Paper Barrier= +Papyrus Block= +Papyrus Door= +Papyrus Ladder= +Papyrus Lattice= +Papyrus Panic= +Place 225 hard snow bricks.= +Place a aggregated diamond block.= +Rich Carpenter= +Sandstone Brick with Golden Edges= +Self-proclaimed Winner= +Silver Sandstone Brick with Golden Edges= +Small Gold-framed Diamond Block= +Soft Dirt Brick= +Soft Snow Brick= +Soft Snow Brick Slab= +Soft Snow Brick Stair= +Steel Brick= +Steel Brick Slab= +Steel Brick Stair= +Stone Brick with Golden Edges= +This block is even harder than diamond; diamond pickaxes can't break it. TNT is able to destroy this block.= +This decorational ice tile has been crafted in a way that it is partially transparent and looks like a real window.= +This is a fence made out of precious goldwood. The fence will neatly connect to its neighbors, making it easy to build nice-looking fence structures. The fence can be jumped over.= +This is a fence rail made out of precious goldwood. It will neatly connect to its neighbors, but without creating fence posts. It can be jumped over.= +Tin Brick= +Tin Brick Slab= +Tin Brick Stair= +Wooden Bar Door= +Wooden Bars= +Yellow Desert= +Papier barriers are thin solid layers of paper which neatly connect to their neighbors as you build them. They could be useful to separate rooms.= +Wooden bars are barriers which neatly connect to their neighbors as you build them.= +Goldglass panes are thin layers of goldglass which neatly connect to their neighbors as you build them.= +Golden windows are decorative blocks which can be placed into holes for nice-looking windows. Golden windows automatically connect to their neighbors as you build them.= +Papyrus lattices are strong barriers which neatly connect to their neighbors as you build them.= +A door covers a vertical area of two blocks to block the way. It can be opened and closed by any player.= +Use the use key on it to open or close it.= +Point the icy steel trapdoor to see who owns it. Use the use key on it to open or close it (if you own it).= +Point the door to see who owns it. Use the use key on the door to open or close it (if you own it).= +Apple Wood Ladder= +Acacia Wood Ladder= +Pine Wood Ladder= +Aspen Wood Ladder= +Jungle Wood Ladder= +Goldwood Ladder= +A piece of wall creates simple barriers that connect to neighbor blocks. Walls can be jumped over.= diff --git a/mods/mtg_plus/mod.conf b/mods/mtg_plus/mod.conf new file mode 100644 index 00000000..3980f910 --- /dev/null +++ b/mods/mtg_plus/mod.conf @@ -0,0 +1,7 @@ +name = mtg_plus +description = Adds various simple decorative blocks, doors, panes and other items to Minetest Game, focusing on snow, ice, luxury, bamboo and more. +depends = default, doors, farming, xpanes, walls, stairs +optional_depends = moreblocks, xdecor, furniture, doc_items, awards +release = 11968 +author = Wuzzy +title = Minetest Game Plus diff --git a/mods/mtg_plus/screenshot.png b/mods/mtg_plus/screenshot.png new file mode 100644 index 00000000..89a37d27 Binary files /dev/null and b/mods/mtg_plus/screenshot.png differ diff --git a/mods/mtg_plus/seethrough.lua b/mods/mtg_plus/seethrough.lua new file mode 100644 index 00000000..c259f22c --- /dev/null +++ b/mods/mtg_plus/seethrough.lua @@ -0,0 +1,61 @@ +local S = minetest.get_translator("mtg_plus") + +-- Blocks that you can see through + +minetest.register_node("mtg_plus:goldglass", { + description = S("Goldglass"), + _doc_items_longdesc = S("A ornamental and mostly transparent block, made by combining glass with gold."), + drawtype = "glasslike_framed_optional", + tiles = {"mtg_plus_goldglass.png", "mtg_plus_goldglass_detail.png"}, + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = { cracky = 3, oddly_breakable_by_hand = 2}, + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:goldglass 1", + recipe = { { "default:gold_ingot", }, + { "default:glass",}, + { "default:gold_ingot", }, + } +}) + +minetest.register_node("mtg_plus:dirty_glass", { + description = S("Dirty Glass"), + _doc_items_longdesc = S("A decorative, semitransparent block. The dirt makes it hard for the sunlight to pass through."), + drawtype = "glasslike_framed_optional", + tiles = {"mtg_plus_dirty_glass.png", "mtg_plus_dirty_glass_detail.png"}, + paramtype = "light", + sunlight_propagates = false, + is_ground_content = false, + groups = {cracky = 3, oddly_breakable_by_hand = 3}, + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:dirty_glass 3", + recipe = { { "", "default:dirt", "" }, + {"default:glass", "default:glass", "default:glass"}, + } +}) + +minetest.register_node("mtg_plus:ice_window", { + description = S("Ice Window"), + _doc_items_longdesc = S("This decorational ice tile has been crafted in a way that it is partially transparent and looks like a real window."), + drawtype = "glasslike", + tiles = {"mtg_plus_ice_window.png"}, + sunlight_propagates = true, + groups = {cracky = 3, cools_lava = 1, slippery = 3 }, + is_ground_content = false, + paramtype = "light", + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:ice_window", + type = "cooking", + recipe = "mtg_plus:ice_tile4", + cooktime = 1, +}) diff --git a/mods/mtg_plus/sounds/mtg_plus_door_ice_close.ogg b/mods/mtg_plus/sounds/mtg_plus_door_ice_close.ogg new file mode 100644 index 00000000..8f37ed21 Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_door_ice_close.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_door_ice_open.ogg b/mods/mtg_plus/sounds/mtg_plus_door_ice_open.ogg new file mode 100644 index 00000000..8a56296d Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_door_ice_open.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_door_icesteel_close.ogg b/mods/mtg_plus/sounds/mtg_plus_door_icesteel_close.ogg new file mode 100644 index 00000000..fb40f8ee Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_door_icesteel_close.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_door_icesteel_open.ogg b/mods/mtg_plus/sounds/mtg_plus_door_icesteel_open.ogg new file mode 100644 index 00000000..55e2d5fb Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_door_icesteel_open.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_paper_dig.1.ogg b/mods/mtg_plus/sounds/mtg_plus_paper_dig.1.ogg new file mode 100644 index 00000000..49ce52a8 Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_paper_dig.1.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_paper_dig.2.ogg b/mods/mtg_plus/sounds/mtg_plus_paper_dig.2.ogg new file mode 100644 index 00000000..d0671282 Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_paper_dig.2.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_paper_dig.3.ogg b/mods/mtg_plus/sounds/mtg_plus_paper_dig.3.ogg new file mode 100644 index 00000000..0ead8612 Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_paper_dig.3.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_paper_dug.1.ogg b/mods/mtg_plus/sounds/mtg_plus_paper_dug.1.ogg new file mode 100644 index 00000000..f760cd14 Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_paper_dug.1.ogg differ diff --git a/mods/mtg_plus/sounds/mtg_plus_paper_step.1.ogg b/mods/mtg_plus/sounds/mtg_plus_paper_step.1.ogg new file mode 100644 index 00000000..6c908f1c Binary files /dev/null and b/mods/mtg_plus/sounds/mtg_plus_paper_step.1.ogg differ diff --git a/mods/mtg_plus/specials.lua b/mods/mtg_plus/specials.lua new file mode 100644 index 00000000..1a6153e8 --- /dev/null +++ b/mods/mtg_plus/specials.lua @@ -0,0 +1,60 @@ +local S = minetest.get_translator("mtg_plus") +local deco +if minetest.get_modpath("doc_items") then + deco = doc.sub.items.temp.deco +end + +-- Special: Variant of dirt without the grass/plant spread +minetest.register_node("mtg_plus:graveldirt", { + description = S("Gravel Dirt"), + _doc_items_longdesc = S("Gravel dirt is a type of dirt consisting of equal parts of gravel and dirt. It combines some of the properties of gravel and dirt."), + tiles = {"mtg_plus_graveldirt.png"}, + is_ground_content = true, + groups = { crumbly = 2, level = 1, soil = 1, }, + sounds = default.node_sound_dirt_defaults(), + drop = { + items = { + { items = { "default:gravel" } }, + { items = { "default:dirt" } }, + } + }, +}) + +minetest.register_craft({ + type = "shapeless", + output = "mtg_plus:graveldirt", + recipe = { "default:gravel", "default:dirt" }, +}) + +-- Special: Extremely hard block +minetest.register_node("mtg_plus:harddiamondblock",{ + description = S("Aggregated Diamond Block"), + _doc_items_longdesc = S("This block is even harder than diamond; diamond pickaxes can't break it. TNT is able to destroy this block."), + tiles = { "mtg_plus_hard_diamond_block.png" }, + is_ground_content = false, + groups = { cracky = 1, level = 4 }, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:harddiamondblock 1", + type = "shapeless", + recipe = { "default:diamondblock", "default:diamondblock" } +}) + +-- Special: Wood that doesn't burn +minetest.register_node("mtg_plus:goldwood", { + description = S("Goldwood"), + _doc_items_longdesc = S("Goldwood is a precious artificial kind of wood made by enriching wood with gold. Goldwood is fireproof and notable for its bright yellowy appearance."), + tiles = {"mtg_plus_goldwood.png"}, + is_ground_content = false, + -- Intentionally not part of the 'wood' group, so it can be used for crafting wood items or as fuel + groups = {choppy = 2}, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_craft({ + output = "mtg_plus:goldwood", + type = "shapeless", + recipe = { "group:wood", "default:gold_ingot" }, +}) diff --git a/mods/mtg_plus/stairslabs.lua b/mods/mtg_plus/stairslabs.lua new file mode 100644 index 00000000..0ecf69aa --- /dev/null +++ b/mods/mtg_plus/stairslabs.lua @@ -0,0 +1,39 @@ +local S = minetest.get_translator("mtg_plus") + +-- Stairs/slabs + +-- Add custom stairs and slabs +local stairslab_ignore_groups = { "wood", "stone", "soil", } + +local function simple_stair_slab(subname, desc_stair, desc_slab) + local itemstring = "mtg_plus:"..subname + local groups = table.copy(minetest.registered_nodes[itemstring].groups) + for i=1,#stairslab_ignore_groups do + groups[stairslab_ignore_groups[i]] = nil + end + stairs.register_stair_and_slab(subname, itemstring, groups, minetest.registered_nodes[itemstring].tiles, desc_stair, desc_slab, minetest.registered_nodes[itemstring].sounds) +end + +simple_stair_slab("sandstone_cobble", S("Cobbled Sandstone Stair"), S("Cobbled Sandstone Slab")) +simple_stair_slab("desert_sandstone_cobble", S("Cobbled Desert Sandstone Stair"), S("Cobbled Desert Sandstone Slab")) +simple_stair_slab("silver_sandstone_cobble", S("Cobbled Silver Sandstone Stair"), S("Cobbled Silver Sandstone Slab")) +simple_stair_slab("jungle_cobble", S("Jungle Cobblestone Stair"), S("Jungle Cobblestone Slab")) +simple_stair_slab("snow_brick", S("Soft Snow Brick Stair"), S("Soft Snow Brick Slab")) +simple_stair_slab("hard_snow_brick", S("Hard Snow Brick Stair"), S("Hard Snow Brick Slab")) +simple_stair_slab("ice_snow_brick", S("Icy Snow Brick Stair"), S("Icy Snow Brick Slab")) +simple_stair_slab("ice_brick", S("Ice Brick Stair"), S("Ice Brick Slab")) +simple_stair_slab("ice_tile4", S("Ice Tile Stair"), S("Ice Tile Slab")) +simple_stair_slab("goldwood", S("Goldwood Stair"), S("Goldwood Slab")) +simple_stair_slab("goldbrick", S("Gold Brick Stair"), S("Gold Brick Slab")) +simple_stair_slab("bronzebrick", S("Bronze Brick Stair"), S("Bronze Brick Slab")) +simple_stair_slab("tinbrick", S("Tin Brick Stair"), S("Tin Brick Slab")) +simple_stair_slab("copperbrick", S("Copper Brick Stair"), S("Copper Brick Slab")) +simple_stair_slab("steelbrick", S("Steel Brick Stair"), S("Steel Brick Slab")) +simple_stair_slab("harddirtbrick", S("Hardened Dirt Brick Stair"), S("Hardened Dirt Brick Slab")) +simple_stair_slab("gravel_cobble", S("Cobbled Gravel Stair"), S("Cobbled Gravel Slab")) + +stairs.register_slab("flint_block", "mtg_plus:flint_block", {cracky=2}, {"mtg_plus_flint_block.png", "mtg_plus_flint_block.png", "mtg_plus_flint_block_slab.png", "mtg_plus_flint_block_slab.png", "mtg_plus_flint_block_slab.png", "mtg_plus_flint_block_slab.png"}, S("Flint Block Slab"), minetest.registered_items["mtg_plus:flint_block"].sounds) +stairs.register_stair("flint_block", "mtg_plus:flint_block", {cracky=2}, {"mtg_plus_flint_block_slab.png", "mtg_plus_flint_block.png", "mtg_plus_flint_block_stair1.png", "mtg_plus_flint_block_stair2.png", "mtg_plus_flint_block.png", "mtg_plus_flint_block_slab.png"}, S("Flint Block Stair"), minetest.registered_items["mtg_plus:flint_block"].sounds) + +stairs.register_slab("ice_block", "mtg_plus:ice_block", {cracky=3, cools_lava=1, slippery=3}, {"mtg_plus_ice_block.png", "mtg_plus_ice_block.png", "mtg_plus_ice_block_slab.png", "mtg_plus_ice_block_slab.png", "mtg_plus_ice_block_slab.png", "mtg_plus_ice_block_slab.png"}, S("Ice Block Slab"), minetest.registered_items["mtg_plus:ice_block"].sounds) +stairs.register_stair("ice_block", "mtg_plus:ice_block", {cracky=3, cools_lava=1, slippery=3}, {"mtg_plus_ice_block_slab.png", "mtg_plus_ice_block.png", "mtg_plus_ice_block_stair1.png", "mtg_plus_ice_block_stair2.png", "mtg_plus_ice_block.png", "mtg_plus_ice_block_slab.png"}, S("Ice Block Stair"), minetest.registered_items["mtg_plus:ice_block"].sounds) diff --git a/mods/mtg_plus/textures/mtg_plus_bronze_brick.png b/mods/mtg_plus/textures/mtg_plus_bronze_brick.png new file mode 100644 index 00000000..793657e8 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_bronze_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_cobble_jungle.png b/mods/mtg_plus/textures/mtg_plus_cobble_jungle.png new file mode 100644 index 00000000..fe6143ad Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_cobble_jungle.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_copper_brick.png b/mods/mtg_plus/textures/mtg_plus_copper_brick.png new file mode 100644 index 00000000..ebf43680 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_copper_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_desert_sandstone_brick_gold.png b/mods/mtg_plus/textures/mtg_plus_desert_sandstone_brick_gold.png new file mode 100644 index 00000000..7de2e04f Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_desert_sandstone_brick_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_desert_sandstone_cobble.png b/mods/mtg_plus/textures/mtg_plus_desert_sandstone_cobble.png new file mode 100644 index 00000000..6b1cc928 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_desert_sandstone_cobble.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_desert_stone_brick_gold.png b/mods/mtg_plus/textures/mtg_plus_desert_stone_brick_gold.png new file mode 100644 index 00000000..18860202 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_desert_stone_brick_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_dirt_brick.png b/mods/mtg_plus/textures/mtg_plus_dirt_brick.png new file mode 100644 index 00000000..999dbf59 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_dirt_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_dirt_brick_hard.png b/mods/mtg_plus/textures/mtg_plus_dirt_brick_hard.png new file mode 100644 index 00000000..0db2208d Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_dirt_brick_hard.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_dirty_glass.png b/mods/mtg_plus/textures/mtg_plus_dirty_glass.png new file mode 100644 index 00000000..e5bf96cb Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_dirty_glass.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_dirty_glass_detail.png b/mods/mtg_plus/textures/mtg_plus_dirty_glass_detail.png new file mode 100644 index 00000000..1d1e596f Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_dirty_glass_detail.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_ice.png b/mods/mtg_plus/textures/mtg_plus_door_ice.png new file mode 100644 index 00000000..7562a3a9 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_ice.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_ice_item.png b/mods/mtg_plus/textures/mtg_plus_door_ice_item.png new file mode 100644 index 00000000..06198889 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_ice_item.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_icesteel.png b/mods/mtg_plus/textures/mtg_plus_door_icesteel.png new file mode 100644 index 00000000..6cdc18d3 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_icesteel.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_icesteel_item.png b/mods/mtg_plus/textures/mtg_plus_door_icesteel_item.png new file mode 100644 index 00000000..7abaecd6 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_icesteel_item.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_papyrus.png b/mods/mtg_plus/textures/mtg_plus_door_papyrus.png new file mode 100644 index 00000000..4da15147 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_papyrus.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_papyrus_item.png b/mods/mtg_plus/textures/mtg_plus_door_papyrus_item.png new file mode 100644 index 00000000..067cc46b Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_papyrus_item.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_wood_bar.png b/mods/mtg_plus/textures/mtg_plus_door_wood_bar.png new file mode 100644 index 00000000..89ba72b1 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_wood_bar.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_door_wood_bar_item.png b/mods/mtg_plus/textures/mtg_plus_door_wood_bar_item.png new file mode 100644 index 00000000..4708d2e6 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_door_wood_bar_item.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_fence_rail_goldwood.png b/mods/mtg_plus/textures/mtg_plus_fence_rail_goldwood.png new file mode 100644 index 00000000..dadada71 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_fence_rail_goldwood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_flint_block.png b/mods/mtg_plus/textures/mtg_plus_flint_block.png new file mode 100644 index 00000000..4f9cd228 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_flint_block.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_flint_block_slab.png b/mods/mtg_plus/textures/mtg_plus_flint_block_slab.png new file mode 100644 index 00000000..bb013681 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_flint_block_slab.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_flint_block_stair1.png b/mods/mtg_plus/textures/mtg_plus_flint_block_stair1.png new file mode 100644 index 00000000..0079f657 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_flint_block_stair1.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_flint_block_stair2.png b/mods/mtg_plus/textures/mtg_plus_flint_block_stair2.png new file mode 100644 index 00000000..941f5b23 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_flint_block_stair2.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_gold_brick.png b/mods/mtg_plus/textures/mtg_plus_gold_brick.png new file mode 100644 index 00000000..d926f160 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_gold_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_gold_diamond_block.png b/mods/mtg_plus/textures/mtg_plus_gold_diamond_block.png new file mode 100644 index 00000000..9ce10469 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_gold_diamond_block.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_goldglass.png b/mods/mtg_plus/textures/mtg_plus_goldglass.png new file mode 100644 index 00000000..709c68e0 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_goldglass.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_goldglass2.png b/mods/mtg_plus/textures/mtg_plus_goldglass2.png new file mode 100644 index 00000000..d1635216 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_goldglass2.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_goldglass_detail.png b/mods/mtg_plus/textures/mtg_plus_goldglass_detail.png new file mode 100644 index 00000000..2ca3d329 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_goldglass_detail.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_goldglass_pane_half.png b/mods/mtg_plus/textures/mtg_plus_goldglass_pane_half.png new file mode 100644 index 00000000..04abcb03 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_goldglass_pane_half.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_goldglass_pane_top.png b/mods/mtg_plus/textures/mtg_plus_goldglass_pane_top.png new file mode 100644 index 00000000..c4aeb725 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_goldglass_pane_top.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_goldwood.png b/mods/mtg_plus/textures/mtg_plus_goldwood.png new file mode 100644 index 00000000..4e96130f Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_goldwood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_gravel_cobble.png b/mods/mtg_plus/textures/mtg_plus_gravel_cobble.png new file mode 100644 index 00000000..688bb4c8 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_gravel_cobble.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_graveldirt.png b/mods/mtg_plus/textures/mtg_plus_graveldirt.png new file mode 100644 index 00000000..0dee7f88 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_graveldirt.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_hard_diamond_block.png b/mods/mtg_plus/textures/mtg_plus_hard_diamond_block.png new file mode 100644 index 00000000..f9f7cb70 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_hard_diamond_block.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_hard_snow_brick.png b/mods/mtg_plus/textures/mtg_plus_hard_snow_brick.png new file mode 100644 index 00000000..a01501c2 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_hard_snow_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_block.png b/mods/mtg_plus/textures/mtg_plus_ice_block.png new file mode 100644 index 00000000..54bea4a4 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_block.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_block_slab.png b/mods/mtg_plus/textures/mtg_plus_ice_block_slab.png new file mode 100644 index 00000000..08acdab2 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_block_slab.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_block_stair.png b/mods/mtg_plus/textures/mtg_plus_ice_block_stair.png new file mode 100644 index 00000000..ffe5b22b Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_block_stair.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_block_stair1.png b/mods/mtg_plus/textures/mtg_plus_ice_block_stair1.png new file mode 100644 index 00000000..7edcc0b2 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_block_stair1.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_block_stair2.png b/mods/mtg_plus/textures/mtg_plus_ice_block_stair2.png new file mode 100644 index 00000000..6044395c Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_block_stair2.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_brick.png b/mods/mtg_plus/textures/mtg_plus_ice_brick.png new file mode 100644 index 00000000..528361f7 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_snow_brick.png b/mods/mtg_plus/textures/mtg_plus_ice_snow_brick.png new file mode 100644 index 00000000..86e22147 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_snow_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_tile16.png b/mods/mtg_plus/textures/mtg_plus_ice_tile16.png new file mode 100644 index 00000000..8c13dbe3 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_tile16.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_tile4.png b/mods/mtg_plus/textures/mtg_plus_ice_tile4.png new file mode 100644 index 00000000..3f2aa18e Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_tile4.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ice_window.png b/mods/mtg_plus/textures/mtg_plus_ice_window.png new file mode 100644 index 00000000..09ade69d Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ice_window.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_jungle_cobble.png b/mods/mtg_plus/textures/mtg_plus_jungle_cobble.png new file mode 100644 index 00000000..d3d2b8c8 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_jungle_cobble.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_acacia_wood.png b/mods/mtg_plus/textures/mtg_plus_ladder_acacia_wood.png new file mode 100644 index 00000000..2f327885 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_acacia_wood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_aspen_wood.png b/mods/mtg_plus/textures/mtg_plus_ladder_aspen_wood.png new file mode 100644 index 00000000..0456c0ad Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_aspen_wood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_bronze.png b/mods/mtg_plus/textures/mtg_plus_ladder_bronze.png new file mode 100644 index 00000000..b091a423 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_bronze.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_copper.png b/mods/mtg_plus/textures/mtg_plus_ladder_copper.png new file mode 100644 index 00000000..568fc6c3 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_copper.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_gold.png b/mods/mtg_plus/textures/mtg_plus_ladder_gold.png new file mode 100644 index 00000000..10e24837 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_goldwood.png b/mods/mtg_plus/textures/mtg_plus_ladder_goldwood.png new file mode 100644 index 00000000..878c9401 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_goldwood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_junglewood.png b/mods/mtg_plus/textures/mtg_plus_ladder_junglewood.png new file mode 100644 index 00000000..15876a38 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_junglewood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_papyrus.png b/mods/mtg_plus/textures/mtg_plus_ladder_papyrus.png new file mode 100644 index 00000000..616bacb6 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_papyrus.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_pine_wood.png b/mods/mtg_plus/textures/mtg_plus_ladder_pine_wood.png new file mode 100644 index 00000000..bb1504ae Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_pine_wood.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_ladder_tin.png b/mods/mtg_plus/textures/mtg_plus_ladder_tin.png new file mode 100644 index 00000000..56bef072 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_ladder_tin.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_obsidian_brick_gold.png b/mods/mtg_plus/textures/mtg_plus_obsidian_brick_gold.png new file mode 100644 index 00000000..e10c2ecd Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_obsidian_brick_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_paperwall.png b/mods/mtg_plus/textures/mtg_plus_paperwall.png new file mode 100644 index 00000000..3c8352db Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_paperwall.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_papyrus_block_side.png b/mods/mtg_plus/textures/mtg_plus_papyrus_block_side.png new file mode 100644 index 00000000..8911664d Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_papyrus_block_side.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_papyrus_block_side2.png b/mods/mtg_plus/textures/mtg_plus_papyrus_block_side2.png new file mode 100644 index 00000000..e3a8b317 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_papyrus_block_side2.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_papyrus_block_y.png b/mods/mtg_plus/textures/mtg_plus_papyrus_block_y.png new file mode 100644 index 00000000..4e31bc19 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_papyrus_block_y.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_papyrus_lattice.png b/mods/mtg_plus/textures/mtg_plus_papyrus_lattice.png new file mode 100644 index 00000000..07ea7aca Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_papyrus_lattice.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_sandstone_brick_gold.png b/mods/mtg_plus/textures/mtg_plus_sandstone_brick_gold.png new file mode 100644 index 00000000..d1a2b052 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_sandstone_brick_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_sandstone_cobble.png b/mods/mtg_plus/textures/mtg_plus_sandstone_cobble.png new file mode 100644 index 00000000..61fd1487 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_sandstone_cobble.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_silver_sandstone_brick_gold.png b/mods/mtg_plus/textures/mtg_plus_silver_sandstone_brick_gold.png new file mode 100644 index 00000000..6fcbf1d3 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_silver_sandstone_brick_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_silver_sandstone_cobble.png b/mods/mtg_plus/textures/mtg_plus_silver_sandstone_cobble.png new file mode 100644 index 00000000..fc71c832 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_silver_sandstone_cobble.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_snow_brick.png b/mods/mtg_plus/textures/mtg_plus_snow_brick.png new file mode 100644 index 00000000..f2acb619 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_snow_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_steel_brick.png b/mods/mtg_plus/textures/mtg_plus_steel_brick.png new file mode 100644 index 00000000..c1a70863 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_steel_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_stone_brick_gold.png b/mods/mtg_plus/textures/mtg_plus_stone_brick_gold.png new file mode 100644 index 00000000..b6a72be0 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_stone_brick_gold.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_tin_brick.png b/mods/mtg_plus/textures/mtg_plus_tin_brick.png new file mode 100644 index 00000000..1b208865 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_tin_brick.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_trapdoor_ice.png b/mods/mtg_plus/textures/mtg_plus_trapdoor_ice.png new file mode 100644 index 00000000..aab2c4e5 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_trapdoor_ice.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_trapdoor_ice_side.png b/mods/mtg_plus/textures/mtg_plus_trapdoor_ice_side.png new file mode 100644 index 00000000..b5c6cfa9 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_trapdoor_ice_side.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_trapdoor_icesteel.png b/mods/mtg_plus/textures/mtg_plus_trapdoor_icesteel.png new file mode 100644 index 00000000..c30da439 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_trapdoor_icesteel.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_trapdoor_icesteel_side.png b/mods/mtg_plus/textures/mtg_plus_trapdoor_icesteel_side.png new file mode 100644 index 00000000..8ad1b0b0 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_trapdoor_icesteel_side.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_wooden_bar.png b/mods/mtg_plus/textures/mtg_plus_wooden_bar.png new file mode 100644 index 00000000..ecf53bfd Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_wooden_bar.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_wooden_bar_side.png b/mods/mtg_plus/textures/mtg_plus_wooden_bar_side.png new file mode 100644 index 00000000..9613634c Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_wooden_bar_side.png differ diff --git a/mods/mtg_plus/textures/mtg_plus_wooden_bar_y.png b/mods/mtg_plus/textures/mtg_plus_wooden_bar_y.png new file mode 100644 index 00000000..6bc362d3 Binary files /dev/null and b/mods/mtg_plus/textures/mtg_plus_wooden_bar_y.png differ diff --git a/mods/mtg_plus/wallfences.lua b/mods/mtg_plus/wallfences.lua new file mode 100644 index 00000000..67c61448 --- /dev/null +++ b/mods/mtg_plus/wallfences.lua @@ -0,0 +1,51 @@ +local S = minetest.get_translator("mtg_plus") + +-- Walls +walls.register("mtg_plus:wall_sandstone_cobble", S("Cobbled Sandstone Wall"), "mtg_plus_sandstone_cobble.png", "mtg_plus:sandstone_cobble", default.node_sound_stone_defaults()) +walls.register("mtg_plus:wall_desert_sandstone_cobble", S("Cobbled Desert Sandstone Wall"), "mtg_plus_desert_sandstone_cobble.png", "mtg_plus:desert_sandstone_cobble", default.node_sound_stone_defaults()) +walls.register("mtg_plus:wall_silver_sandstone_cobble", S("Cobbled Silver Sandstone Wall"), "mtg_plus_silver_sandstone_cobble.png", "mtg_plus:silver_sandstone_cobble", default.node_sound_stone_defaults()) +walls.register("mtg_plus:wall_jungle_cobble", S("Jungle Cobblestone Wall"), "mtg_plus_jungle_cobble.png", "mtg_plus:jungle_cobble", default.node_sound_stone_defaults()) +walls.register("mtg_plus:wall_ice_tile16", S("Dense Ice Tile Wall"), "mtg_plus_ice_tile16.png", "mtg_plus:ice_tile16", default.node_sound_glass_defaults()) +walls.register("mtg_plus:wall_gravel_cobble", S("Cobbled Gravel Wall"), "mtg_plus_gravel_cobble.png", "mtg_plus:gravel_cobble", default.node_sound_stone_defaults()) + + +do + local longdesc = S("A piece of wall creates simple barriers that connect to neighbor blocks. Walls can be jumped over.") + + local groups = minetest.registered_items["mtg_plus:wall_ice_tile16"].groups + groups.stone = nil + groups.slippery = 1 + minetest.override_item("mtg_plus:wall_ice_tile16", { groups = groups, _doc_items_longdesc = longdesc }) + + groups = minetest.registered_items["mtg_plus:wall_sandstone_cobble"].groups + groups.stone = nil + minetest.override_item("mtg_plus:wall_sandstone_cobble", { groups = groups, _doc_items_longdesc = longdesc }) + + minetest.override_item("mtg_plus:wall_desert_sandstone_cobble", { _doc_items_longdesc = longdesc }) + minetest.override_item("mtg_plus:wall_silver_sandstone_cobble", { _doc_items_longdesc = longdesc }) + minetest.override_item("mtg_plus:wall_jungle_cobble", { _doc_items_longdesc = longdesc }) + minetest.override_item("mtg_plus:wall_gravel_cobble", { _doc_items_longdesc = longdesc }) +end + +-- Fences +default.register_fence("mtg_plus:fence_goldwood", { + description = S("Goldwood Fence"), + _doc_items_longdesc = S("This is a fence made out of precious goldwood. The fence will neatly connect to its neighbors, making it easy to build nice-looking fence structures. The fence can be jumped over."), + texture = "mtg_plus_goldwood.png", + material = "mtg_plus:goldwood", + sounds = minetest.registered_nodes["mtg_plus:goldwood"].sounds, + groups = {choppy=2, }, +}) + +default.register_fence_rail("mtg_plus:fence_rail_goldwood", { + description = S("Goldwood Fence Rail"), + _doc_items_longdesc = S("This is a fence rail made out of precious goldwood. It will neatly connect to its neighbors, but without creating fence posts. It can be jumped over."), + texture = "mtg_plus_fence_rail_goldwood.png", + inventory_image = "default_fence_rail_overlay.png^mtg_plus_goldwood.png^" .. + "default_fence_rail_overlay.png^[makealpha:255,126,126", + wield_image = "default_fence_rail_overlay.png^mtg_plus_goldwood.png^" .. + "default_fence_rail_overlay.png^[makealpha:255,126,126", + material = "mtg_plus:goldwood", + groups = {choppy = 2, }, + sounds = minetest.registered_nodes["mtg_plus:goldwood"].sounds, +}) diff --git a/mods/mtg_plus/xpanes.lua b/mods/mtg_plus/xpanes.lua new file mode 100644 index 00000000..6cd37adc --- /dev/null +++ b/mods/mtg_plus/xpanes.lua @@ -0,0 +1,144 @@ +local S = minetest.get_translator("mtg_plus") + +-- xpanes +xpanes.register_pane("paper", { + description = S("Paper Barrier"), + inventory_image = "mtg_plus_paperwall.png", + wield_image = "mtg_plus_paperwall.png", + textures = {"mtg_plus_paperwall.png", "mtg_plus_paperwall.png", "mtg_plus_paperwall.png"}, + groups = {snappy=3, flammable=4, pane=1}, + sounds = { + footstep = {name="mtg_plus_paper_step", gain=0.1, max_hear_distance=7}, + place = {name="mtg_plus_paper_step", gain=0.3, max_hear_distance=13}, + dig = {name="mtg_plus_paper_dig", gain=0.1, max_hear_distance=11}, + dug = {name="mtg_plus_paper_dug", gain=0.2, max_hear_distance=13}, + }, + recipe = { + { "default:paper", "default:paper", "default:paper" }, + { "default:paper", "default:paper", "default:paper" }, + } +}) + +local r +if minetest.registered_items["xpanes:paper_flat"] then + r = "xpanes:paper_flat" +else + r = "xpanes:paper" +end +minetest.register_craft({ + type = "fuel", + recipe = r, + burntime = 1, +}) + +xpanes.register_pane("wood", { + description = S("Wooden Bars"), + inventory_image = "mtg_plus_wooden_bar.png", + wield_image = "mtg_plus_wooden_bar.png", + textures = {"mtg_plus_wooden_bar.png", "mtg_plus_wooden_bar_side.png", "mtg_plus_wooden_bar_y.png"}, + groups = {choppy=3, oddly_breakable_by_hand=2, flammable=2, pane=1}, + sounds = default.node_sound_wood_defaults(), + recipe = { + { "group:wood", "", "group:wood" }, + { "group:wood", "", "group:wood" }, + { "group:wood", "", "group:wood" }, + } +}) + +if minetest.registered_items["xpanes:wood_flat"] then + r = "xpanes:wood_flat" +else + r = "xpanes:wood" +end +minetest.register_craft({ + type = "fuel", + recipe = r, + burntime = 2, +}) + +xpanes.register_pane("goldglass", { + description = S("Goldglass Pane"), + inventory_image = "mtg_plus_goldglass.png", + wield_image = "mtg_plus_goldglass.png", + textures = {"mtg_plus_goldglass.png","mtg_plus_goldglass_pane_half.png","mtg_plus_goldglass_pane_top.png",}, + groups = {cracky=3,oddly_breakable_by_hand=2,pane=1}, + sounds = default.node_sound_glass_defaults(), + recipe = { + { "mtg_plus:goldglass","mtg_plus:goldglass","mtg_plus:goldglass", }, + { "mtg_plus:goldglass","mtg_plus:goldglass","mtg_plus:goldglass", }, + } +}) + +xpanes.register_pane("goldglass2", { + description = S("Golden Window"), + inventory_image = "mtg_plus_goldglass2.png", + wield_image = "mtg_plus_goldglass2.png", + textures = {"mtg_plus_goldglass2.png","mtg_plus_goldglass_pane_half.png","mtg_plus_goldglass_pane_top.png",}, + groups = {cracky=3,oddly_breakable_by_hand=3,pane=1}, + sounds = default.node_sound_glass_defaults(), + recipe = { + { "default:gold_ingot","default:gold_ingot","default:gold_ingot", }, + { "default:gold_ingot","default:glass","default:gold_ingot", }, + { "default:gold_ingot","default:gold_ingot","default:gold_ingot", }, + } +}) + +xpanes.register_pane("papyrus", { + description = S("Papyrus Lattice"), + inventory_image = "mtg_plus_papyrus_lattice.png", + wield_image = "mtg_plus_papyrus_lattice.png", + textures = {"mtg_plus_papyrus_lattice.png","mtg_plus_papyrus_lattice.png","mtg_plus_papyrus_lattice.png"}, + groups = {snappy=2, choppy=1, flammable=2,pane=1}, + sounds = default.node_sound_leaves_defaults(), + recipe = { + { "default:papyrus", "farming:string", "default:papyrus" }, + { "default:papyrus", "farming:string", "default:papyrus" }, + } +}) + +if minetest.registered_items["xpanes:papyrus_flat"] then + r = "xpanes:papyrus_flat" +else + r = "xpanes:papyrus" +end +minetest.register_craft({ + type = "fuel", + recipe = r, + burntime = 1, +}) + + +xpanes.register_pane("ice", { + description = S("Ice Window Pane"), + inventory_image = "mtg_plus_ice_window.png", + wield_image = "mtg_plus_ice_window.png", + textures = {"mtg_plus_ice_window.png", "mtg_plus_ice_window.png", "mtg_plus_ice_window.png"}, + groups = {cracky=3, slippery = 3, pane=1}, + sounds = default.node_sound_glass_defaults(), + recipe = { + { "mtg_plus:ice_window", "mtg_plus:ice_window", "mtg_plus:ice_window", }, + { "mtg_plus:ice_window", "mtg_plus:ice_window", "mtg_plus:ice_window", } + } +}) + +local panes = { + paper = S("Papier barriers are thin solid layers of paper which neatly connect to their neighbors as you build them. They could be useful to separate rooms."), + wood = S("Wooden bars are barriers which neatly connect to their neighbors as you build them."), + goldglass = S("Goldglass panes are thin layers of goldglass which neatly connect to their neighbors as you build them."), + goldglass2 = S("Golden windows are decorative blocks which can be placed into holes for nice-looking windows. Golden windows automatically connect to their neighbors as you build them."), + papyrus = S("Papyrus lattices are strong barriers which neatly connect to their neighbors as you build them."), + ice = S("Ice window panes are thinner than the full ice windows and neatly connect to each other as you build them"), +} + +for pane, longdesc in pairs(panes) do + minetest.override_item("xpanes:"..pane.."_flat", { + _doc_items_longdesc = longdesc, + }) + minetest.override_item("xpanes:"..pane, { + _doc_items_create_entry = false, + }) + if minetest.get_modpath("doc") then + doc.add_entry_alias("nodes", "xpanes:"..pane.."_flat", "nodes", "xpanes:"..pane) + end +end + diff --git a/mods/spawn_command/LICENSE b/mods/spawn_command/LICENSE new file mode 100644 index 00000000..40c8ae62 --- /dev/null +++ b/mods/spawn_command/LICENSE @@ -0,0 +1,505 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +(This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.) + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + {signature of Ty Coon}, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/mods/spawn_command/depends.txt b/mods/spawn_command/depends.txt new file mode 100644 index 00000000..1d1275c2 --- /dev/null +++ b/mods/spawn_command/depends.txt @@ -0,0 +1,2 @@ +default +cursed_world? diff --git a/mods/spawn_command/description.txt b/mods/spawn_command/description.txt new file mode 100644 index 00000000..9c9cad37 --- /dev/null +++ b/mods/spawn_command/description.txt @@ -0,0 +1 @@ +Adds a /spawn command that telepors you to the server spawn command. diff --git a/mods/spawn_command/init.lua b/mods/spawn_command/init.lua new file mode 100644 index 00000000..f2069246 --- /dev/null +++ b/mods/spawn_command/init.lua @@ -0,0 +1,38 @@ + +spawn_command = {} +spawn_command.pos = {x=0, y=3, z=0} +local cursed_world_exists = minetest.get_modpath("cursed_world") + +if minetest.setting_get_pos("static_spawnpoint") then + spawn_command.pos = minetest.setting_get_pos("static_spawnpoint") +end + +function teleport_to_spawn(name) + local player = minetest.get_player_by_name(name) + if player == nil then + -- just a check to prevent the server crashing + return false + end + local pos = player:get_pos() + if math.abs(spawn_command.pos.x-pos.x)<20 and math.abs(spawn_command.pos.z-pos.z)<20 then + minetest.chat_send_player(name, "Already close to spawn!") + elseif cursed_world_exists and _G['cursed_world'] ~= nil and --check global table for cursed_world mod + cursed_world.location_y and cursed_world.dimension_y and + pos.y < (cursed_world.location_y + cursed_world.dimension_y) and --if player is in cursed world, stay in cursed world + pos.y > (cursed_world.location_y - cursed_world.dimension_y) + then --check global table for cursed_world mod + --minetest.chat_send_player(name, "T"..(cursed_world.location_y + cursed_world.dimension_y).." "..(cursed_world.location_y - cursed_world.dimension_y)) + local spawn_pos = vector.round(spawn_command.pos); + spawn_pos.y = spawn_pos.y + cursed_world.location_y; + player:set_pos(spawn_pos) + minetest.chat_send_player(name, "Teleported to spawn!") + else + player:set_pos(spawn_command.pos) + minetest.chat_send_player(name, "Teleported to spawn!") + end +end + +minetest.register_chatcommand("spawn", { + description = "Teleport you to spawn point.", + func = teleport_to_spawn, +}) diff --git a/mods/spawn_command/mod.conf b/mods/spawn_command/mod.conf new file mode 100644 index 00000000..5518a53f --- /dev/null +++ b/mods/spawn_command/mod.conf @@ -0,0 +1,5 @@ +name = spawn_command +release = 8098 +author = lag01 +description = Adds a /spawn command that teleports you to the server spawn command. +title = Spawn Command diff --git a/mods/steampunk_blimp/.luacheckrc b/mods/steampunk_blimp/.luacheckrc new file mode 100644 index 00000000..b5f54a07 --- /dev/null +++ b/mods/steampunk_blimp/.luacheckrc @@ -0,0 +1,23 @@ +unused_args = false +allow_defined_top = true + +globals = { + "minetest", + "airutils", + "core", + "player_api", + "math.sign", +} + +read_globals = { + string = {fields = {"split"}}, + table = {fields = {"copy", "getn"}}, + + -- Builtin + "vector", "ItemStack", + "dump", "DIR_DELIM", "VoxelArea", "Settings", + + -- MTG + "default", "sfinv", "creative", +} + diff --git a/mods/steampunk_blimp/LICENSE b/mods/steampunk_blimp/LICENSE new file mode 100644 index 00000000..960a1dda --- /dev/null +++ b/mods/steampunk_blimp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 APercy - Alexsandro Percy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/mods/steampunk_blimp/README.md b/mods/steampunk_blimp/README.md new file mode 100644 index 00000000..fd06b6c5 --- /dev/null +++ b/mods/steampunk_blimp/README.md @@ -0,0 +1,113 @@ +Minetest 5.4 mod: Steampunk Blimp +======================================== + +This mod implements a fantasy steampunk blimp for minetest. +The mod was made for fun, but tries to provide an immersion on it's operation. +It can carry 7 people. + +To fly it, it is necessary to provide some items, such as fuel to be burned and +water for the boiler. The fuel can be coal, coal block and wood. To supply it, +be on board and punch the necessary items on the airship. There is another way to +load water to the boiler: if it is landed on water, it can load it through the +menu. But the current pressure will be lost. + +Activate the furnace in the first option of the menu. Take control by activating +the option "Take the Control". + +The information panel will be on the left and bottom of the screen. Wait for the +boiler to reach the proper pressure for operation (green) before operating the power lever. +To go up, press Jump (space). Note that it takes some pressure from the boiler. To go down, +hold sneak (shift). + +Forward increases the propeller power, Backward reduces. +There is a power mode. When the lever reaches the up limit, hold E +and forward to increase the acceleration. But note that the boiler will lose pressure. +The blimp inventory can be accessed by Aux (E) + rightclick. + +Shared owners: +This vehicle was made to be shared with a team. So the owner can set more users to +operate it. Inside the blimp, just use the command /blimp_share + +- To go reverse, hold aux (E key) and backward together. +- To remove someone from the sharing, /blimp_remove +- To list the owners, /blimp_list +- To lock the blimp access, so only the owners can enter: /blimp_lock true +- To let anyone enter, /blimp_lock false + +All shared owners can access the blimp inventory + +Painting: +As the planes, punch a dye against the hull, so the primary color will change +- To change the secondary color, punch a dye, but holding Aux (E) key. +It is possible to set a logo on your blimp, so enter inside it and type the command /blimp_logo +Only the original owner can do the paintings + +Shortcuts: + +punch with dye to paint +forward and backward while in drive position: controls the power lever +left and right while in drive position: controls the direction +jump and sneak: controls the up and down movement + +- right click to enter and access menu +- E + right click while inside: acess inventory +- E + backward while in drive position: the machine does backward +- E + foward while in drive position: extra power + +Tip: +Drive it gently. +The captain can leave the drive position to walk too +If a player goes timeout or logoff in flight, the blimp will "rescue" him if no other player +enter the blimp, so is a good idea wait the friend at a secure place far from anyone who +wants to enter the blimp. + +Know issues: +The walk movement inside the ship is affected by server lag, because the lack of +an interpolation method on attach function. + +Rubber-band bug is from minetest nature, just close and reopen minetest to solve. +Or try to live the Michael Jackson's way of life and learn the Moonwalk properly XD +Some old versions of minetest can have an strange issue, the camera is set to +the map center. So if it happens, just type /blimp_eject to be free again. + + +License of source code: +MIT (see file LICENSE) + +License of media (textures and sounds): +--------------------------------------- +collision.ogg by APercy, CC0 + +Blimp model and textures by APercy. CC BY-SA 3.0 + +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) +Copyright (C) 2022 Alexsandro Percy (APercy) + +You are free to: +Share — copy and redistribute the material in any medium or format. +Adapt — remix, transform, and build upon the material for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and +indicate if changes were made. You may do so in any reasonable manner, but not in any way +that suggests the licensor endorses you or your use. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute +your contributions under the same license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that +legally restrict others from doing anything the license permits. + +Notices: + +You do not have to comply with the license for elements of the material in the public +domain or where your use is permitted by an applicable exception or limitation. +No warranties are given. The license may not give you all of the permissions necessary +for your intended use. For example, other rights such as publicity, privacy, or moral +rights may limit how you use the material. + +For more details: +http://creativecommons.org/licenses/by-sa/3.0/ + diff --git a/mods/steampunk_blimp/control.lua b/mods/steampunk_blimp/control.lua new file mode 100644 index 00000000..e9b0dd95 --- /dev/null +++ b/mods/steampunk_blimp/control.lua @@ -0,0 +1,158 @@ +--global constants + +steampunk_blimp.vector_up = vector.new(0, 1, 0) + +function steampunk_blimp.check_node_below(obj) + local pos_below = obj:get_pos() + pos_below.y = pos_below.y - 0.1 + local node_below = minetest.get_node(pos_below).name + local nodedef = minetest.registered_nodes[node_below] + local touching_ground = not nodedef or -- unknown nodes are solid + nodedef.walkable or false + local liquid_below = not touching_ground and nodedef.liquidtype ~= "none" + return touching_ground, liquid_below +end + +function steampunk_blimp.powerAdjust(self,dtime,factor,dir,max_power) + local max = max_power or 100 + local add_factor = factor/2 + add_factor = add_factor * (dtime/steampunk_blimp.ideal_step) --adjusting the command speed by dtime + + if dir == 1 then + if self._power_lever < max then + self._power_lever = self._power_lever + add_factor + end + if self._power_lever > max then + self._power_lever = max + end + end + if dir == -1 then + self._power_lever = self._power_lever - add_factor + if self._power_lever < -15 then self._power_lever = -15 end + end +end + +function steampunk_blimp.control(self, dtime, hull_direction, longit_speed, accel) + if self._last_time_command == nil then self._last_time_command = 0 end + self._last_time_command = self._last_time_command + dtime + if self._last_time_command > 1 then self._last_time_command = 1 end + local player = nil + if self.driver_name then + player = minetest.get_player_by_name(self.driver_name) + end + local retval_accel = accel; + + -- player control + local ctrl = nil + if player and self._at_control == true then + ctrl = player:get_player_control() + + if self.anchored == false then + local factor = 1 + if ctrl.up then + local can_acc = true + if self._power_lever >= 82 then can_acc = false end + if ctrl.aux1 then can_acc = true end + if can_acc then + steampunk_blimp.powerAdjust(self, dtime, factor, 1) + end + elseif ctrl.down then + steampunk_blimp.powerAdjust(self, dtime, factor, -1) + end + else + --anchor away, so stop! + self._power_lever = 0 + end + if not ctrl.aux1 and self._power_lever < 0 then self._power_lever = 0 end + + self._is_going_up = false + if ctrl.jump then + if self._boiler_pressure > 0 then + self._baloon_buoyancy = 1.02 + if self.isinliquid then self._baloon_buoyancy = 1.10 end + end + self._is_going_up = true + elseif ctrl.sneak then + self._baloon_buoyancy = -1.02 + end + + -- rudder + local rudder_limit = 30 + local speed = 10 + if ctrl.right then + self._rudder_angle = math.max(self._rudder_angle-speed*dtime,-rudder_limit) + elseif ctrl.left then + self._rudder_angle = math.min(self._rudder_angle+speed*dtime,rudder_limit) + end + end + + --make the blimp loss height when without pressure (and not anchored) + if self.anchored == false and not self.isinliquid then + if self._boiler_pressure <= 0 then + self._baloon_buoyancy = -0.2 + end + end + + --engine acceleration calc + local engineacc = (self._power_lever * steampunk_blimp.max_engine_acc) / 100; + + --do not exceed + local max_speed = 3 + if longit_speed > max_speed then + engineacc = engineacc - (longit_speed-max_speed) + end + + if engineacc ~= nil then + retval_accel=vector.add(accel,vector.multiply(hull_direction,engineacc)) + end + --minetest.chat_send_all('paddle: '.. paddleacc) + + + if longit_speed > 0 then + if ctrl then + if not ctrl.right or not ctrl.left then + steampunk_blimp.rudder_auto_correction(self, longit_speed, dtime) + end + else + steampunk_blimp.rudder_auto_correction(self, longit_speed, dtime) + end + end + + steampunk_blimp.buoyancy_auto_correction(self, self.dtime) + + return retval_accel +end + +function steampunk_blimp.rudder_auto_correction(self, longit_speed, dtime) + local factor = 1 + if self._rudder_angle > 0 then factor = -1 end + local correction = (steampunk_blimp.rudder_limit*(longit_speed/2000)) * factor * (dtime/steampunk_blimp.ideal_step) + local before_correction = self._rudder_angle + local new_rudder_angle = self._rudder_angle + correction + if math.sign(before_correction) ~= math.sign(new_rudder_angle) then + self._rudder_angle = 0 + else + self._rudder_angle = new_rudder_angle + end +end + +function steampunk_blimp.buoyancy_auto_correction(self, dtime) + local factor = 1 + --minetest.chat_send_player(self.driver_name, "antes: " .. self._baloon_buoyancy) + if self._baloon_buoyancy > 0 then factor = -1 end + local time_correction = (dtime/steampunk_blimp.ideal_step) + if time_correction < 1 then time_correction = 1 end + local intensity = 0.2 + local correction = (intensity*factor) * time_correction + if math.abs(correction) > 0.5 then correction = 0.5 * math.sign(correction) end + --minetest.chat_send_player(self.driver_name, correction) + local before_correction = self._baloon_buoyancy + local new_baloon_buoyancy = self._baloon_buoyancy + correction + if math.sign(before_correction) ~= math.sign(new_baloon_buoyancy) then + self._baloon_buoyancy = 0 + else + self._baloon_buoyancy = new_baloon_buoyancy + end + --minetest.chat_send_player(self.driver_name, "depois: " .. self._baloon_buoyancy) +end + diff --git a/mods/steampunk_blimp/custom_physics.lua b/mods/steampunk_blimp/custom_physics.lua new file mode 100644 index 00000000..29ccf16f --- /dev/null +++ b/mods/steampunk_blimp/custom_physics.lua @@ -0,0 +1,96 @@ +local min = math.min +local abs = math.abs + +function steampunk_blimp.physics(self) + local friction = 0.996 + local vel=self.object:get_velocity() + -- dumb friction + if self.isonground and not self.isinliquid then + --minetest.chat_send_all("with friction") + vel = {x=vel.x*friction, + y=vel.y, + z=vel.z*friction} + self.object:set_velocity(vel) + end + + -- bounciness + if self.springiness and self.springiness > 0 then + local vnew = vector.new(vel) + + if not self.collided then -- ugly workaround for inconsistent collisions + for _,k in ipairs({'y','z','x'}) do + if vel[k]==0 and abs(self.lastvelocity[k])> 0.1 then + vnew[k]=-self.lastvelocity[k]*self.springiness + end + end + end + + if not vector.equals(vel,vnew) then + self.collided = true + else + if self.collided then + vnew = vector.new(self.lastvelocity) + end + self.collided = false + end + --minetest.chat_send_all("vnew") + self.object:set_velocity(vnew) + end + --[[else + self.object:set_pos(self.object:get_pos()) + if not self.isonground then + --minetest.chat_send_all("test") + self.object:set_velocity(vel) + end + end]]-- + + --buoyancy + local surface = nil + local spos = airutils.get_stand_pos(self) + spos.y = spos.y+0.01 + -- get surface height + local snodepos = airutils.get_node_pos(spos) + local surfnode = airutils.nodeatpos(spos) + while surfnode and (surfnode.drawtype == 'liquid' or surfnode.drawtype == 'flowingliquid') do + surface = snodepos.y +0.5 + if surface > spos.y+self.height then break end + snodepos.y = snodepos.y+1 + surfnode = airutils.nodeatpos(snodepos) + end + + local accell + self.water_drag = 0.1 + self.object:move_to(self.object:get_pos()) + local time_correction = (self.dtime/steampunk_blimp.ideal_step) + if time_correction < 1 then time_correction = 1 end + local y_accel = self._baloon_buoyancy*time_correction + local max_y_acell = 0.6 + if y_accel > max_y_acell then y_accel = max_y_acell end + if y_accel < (-1*max_y_acell) then y_accel = -1*max_y_acell end + + + if surface then + self.isinliquid = true + local height = self.height + local submergence = min(surface-spos.y,height)/height +-- local balance = self.buoyancy*self.height + local buoyacc = airutils.gravity*(self.buoyancy-submergence) + --local buoyacc = self._baloon_buoyancy*(self.buoyancy-submergence) + accell = {x=-vel.x*self.water_drag,y=buoyacc-(vel.y*abs(vel.y)*0.4),z=-vel.z*self.water_drag} + --local v_accell = {x=0,y=buoyacc-(vel.y*abs(vel.y)*0.4),z=0} + airutils.set_acceleration(self.object,accell) + if self._baloon_buoyancy > 0 then + airutils.set_acceleration(self.object,{x=0,y=y_accel,z=0}) + end + else + self.isinliquid = false + if self._baloon_buoyancy == 0 then + local velocity = vector.new(vel) + velocity.y = velocity.y - (velocity.y/100) + self.object:set_velocity(velocity) + end + --minetest.chat_send_all("_baloon_buoyancy: "..self._baloon_buoyancy.." - dtime: "..self.dtime.." - ideal: "..steampunk_blimp.ideal_step) + airutils.set_acceleration(self.object,{x=0,y=y_accel,z=0}) + end + +end diff --git a/mods/steampunk_blimp/engine_management.lua b/mods/steampunk_blimp/engine_management.lua new file mode 100644 index 00000000..20e0b068 --- /dev/null +++ b/mods/steampunk_blimp/engine_management.lua @@ -0,0 +1,95 @@ +steampunk_blimp.PRESSURE_CONSUMPTION = 500 + +local adjust_variable = 500 +local lost_power = (1/steampunk_blimp.FUEL_CONSUMPTION)*adjust_variable +local gained_pressure = (2/steampunk_blimp.FUEL_CONSUMPTION)*adjust_variable + +local lost_water = (1/steampunk_blimp.WATER_CONSUMPTION) + +steampunk_blimp.boiler_min = 155 +steampunk_blimp.boiler_max = 310 + +function steampunk_blimp.start_boiler(self) + if self._boiler_pressure < 150 then + -- sound and animation + if self.sound_handle_pistons then + minetest.sound_stop(self.sound_handle_pistons) + self.sound_handle_pistons = nil + end + elseif self._boiler_pressure >= 150 then + -- sound + --minetest.chat_send_all(dump(self.sound_handle_pistons)) + if self.sound_handle_pistons == nil then + if self.object then + self.sound_handle_pistons = minetest.sound_play({name = steampunk_blimp.piston_sound.name},--"default_item_smoke"}, + {object = self.object, gain = steampunk_blimp.piston_sound.gain, + pitch = steampunk_blimp.piston_sound.pitch, + max_hear_distance = 32, + loop = true,}) + end + end + end +end + +local function boiler_step(self, accel) + steampunk_blimp.start_boiler(self) + + local consumed_pressure = self._power_lever/steampunk_blimp.PRESSURE_CONSUMPTION + if self._engine_running == false then consumed_pressure = consumed_pressure + lost_power end + + if self._boiler_pressure > steampunk_blimp.boiler_max then self._boiler_pressure = steampunk_blimp.boiler_max end + if self._boiler_pressure > steampunk_blimp.boiler_min then + --[[-- sound and animation + steampunk_blimp.engineSoundPlay(self) + self.object:set_animation_frame_speed(steampunk_blimp.iddle_rotation)]]-- + + steampunk_blimp.engine_set_sound_and_animation(self) + self._water_level = self._water_level - lost_water + end + if self._boiler_pressure < steampunk_blimp.boiler_min then + self._power_lever = 0 + --if self.sound_handle_pistons then minetest.sound_stop(self.sound_handle_pistons) end + self.object:set_animation_frame_speed(0) + end + + self._boiler_pressure = self._boiler_pressure - consumed_pressure + --lets lose more pressure if it's going up + if self._is_going_up == true then + --minetest.chat_send_all("subindo "..consumed_pressure) + self._boiler_pressure = self._boiler_pressure - (200/steampunk_blimp.PRESSURE_CONSUMPTION) + end + + if self._boiler_pressure < 0 then self._boiler_pressure = 0 end + if self._water_level < 0 then self._water_level = 0 end +end + +local function furnace_step(self, accel) + if self._energy > 0 and self._engine_running then + local consumed_power = (1/steampunk_blimp.FUEL_CONSUMPTION) + local time_correction = (self.dtime/steampunk_blimp.ideal_step) + if time_correction < 1 then time_correction = 1 end + local dtimed_pressure = gained_pressure*time_correction + self._boiler_pressure = self._boiler_pressure + dtimed_pressure --pressure for the boiler + self._energy = self._energy - consumed_power; --removes energy + end + if self._energy <= 0 or self._water_level <= 0 then + self._engine_running = false + if self.sound_handle then minetest.sound_stop(self.sound_handle) end + end +end + +function steampunk_blimp.engine_step(self, accel) + furnace_step(self, accel) + boiler_step(self, accel) + + if self.driver_name then + local player = minetest.get_player_by_name(self.driver_name) + + local pressure = steampunk_blimp.get_pointer_angle(self._boiler_pressure, 200 ) + local water = steampunk_blimp.get_pointer_angle(self._water_level, steampunk_blimp.MAX_WATER) + local coal = self._energy + --minetest.chat_send_all(self._power_lever) + steampunk_blimp.update_hud(player, coal, 180-water, -pressure, self._power_lever) + end +end + diff --git a/mods/steampunk_blimp/entities.lua b/mods/steampunk_blimp/entities.lua new file mode 100644 index 00000000..e6867769 --- /dev/null +++ b/mods/steampunk_blimp/entities.lua @@ -0,0 +1,612 @@ +-- +-- constants +-- +local LONGIT_DRAG_FACTOR = 0.13*0.13 +local LATER_DRAG_FACTOR = 2.0 + +-- +-- entity +-- + +core.register_entity('steampunk_blimp:fire',{ +initial_properties = { + physical = false, + collide_with_objects=false, + pointable=false, + glow = 0, + visual = "mesh", + mesh = "steampunk_blimp_light.b3d", + textures = { + "steampunk_blimp_alpha.png", + }, + }, + + on_activate = function(self,std) + self.sdata = core.deserialize(std) or {} + if self.sdata.remove then self.object:remove() end + end, + + get_staticdata=function(self) + self.sdata.remove=true + return core.serialize(self.sdata) + end, + +}) + +-- +-- seat pivot +-- +core.register_entity('steampunk_blimp:stand_base',{ + initial_properties = { + physical = false, + collide_with_objects=false, + collisionbox = {-2, -2, -2, 2, 0, 2}, + pointable=false, + visual = "mesh", + mesh = "steampunk_blimp_stand_base.b3d", + textures = {"steampunk_blimp_alpha.png",}, + }, + dist_moved = 0, + + on_activate = function(self,std) + self.sdata = core.deserialize(std) or {} + if self.sdata.remove then self.object:remove() end + end, + + get_staticdata=function(self) + self.sdata.remove=true + return core.serialize(self.sdata) + end, +}) + +core.register_entity("steampunk_blimp:blimp", { + initial_properties = { + physical = true, + collide_with_objects = true, --true, + collisionbox = {-4, -2.5, -4, 4, 9, 4}, --{-1,0,-1, 1,0.3,1}, + --selectionbox = {-0.6,0.6,-0.6, 0.6,1,0.6}, + visual = "mesh", + backface_culling = false, + mesh = "steampunk_blimp.b3d", + textures = steampunk_blimp.textures_copy(), + }, + textures = {}, + driver_name = nil, + sound_handle = nil, + static_save = true, + infotext = "A nice blimp", + lastvelocity = vector.new(), + hp = 50, + color = "blue", + color2 = "white", + logo = "steampunk_blimp_alpha_logo.png", + timeout = 0; + buoyancy = 0.15, + max_hp = 50, + anchored = true, + physics = steampunk_blimp.physics, + hull_integrity = nil, + owner = "", + time_total = 0, + _shared_owners = {}, + _engine_running = false, + _power_lever = 0, + _last_applied_power = 0, + _at_control = false, + _rudder_angle = 0, + _baloon_buoyancy = 0, + _show_hud = true, + _energy = 1.0,--0.001, + _water_level = 1.0, + _boiler_pressure = 1.0, --min 155 max 310 + _is_going_up = false, --to tell the boiler to lose pressure + _passengers = {}, --passengers list + _passengers_base = {}, --obj id + _passengers_base_pos = steampunk_blimp.copy_vector({}), + _passenger_is_sit = {}, -- 0, 1, 2, 3 or 4 ==> stand, 0, 90, 180, 270 --the sit rotation + _passengers_locked = false, + _disconnection_check_time = 0, + _inv = nil, + _inv_id = "", + _ship_name = "", + _name_color = 0, + _name_hor_aligment = 3.0, + item = "steampunk_blimp:blimp", + _vehicle_name = "Steampunk Blimp", + + get_staticdata = function(self) -- unloaded/unloads ... is now saved + return core.serialize({ + stored_baloon_buoyancy = self._baloon_buoyancy, + stored_energy = self._energy, + stored_water_level = self._water_level, + stored_boiler_pressure = self._boiler_pressure, + stored_owner = self.owner, + stored_shared_owners = self._shared_owners, + stored_hp = self.hp, + stored_color = self.color, + stored_color2 = self.color2, + stored_logo = self.logo, + stored_anchor = self.anchored, + stored_hull_integrity = self.hull_integrity, + stored_item = self.item, + stored_inv_id = self._inv_id, + stored_passengers = self._passengers, --passengers list + stored_passengers_locked = self._passengers_locked, + stored_ship_name = self._ship_name, + stored_vehicle_name = self._vehicle_name, + remove = self._remove or false, + }) + end, + + on_deactivate = function(self) + if self._remove ~= true then + airutils.save_inventory(self) + end + if self.sound_handle then core.sound_stop(self.sound_handle) end + if self.sound_handle_pistons then core.sound_stop(self.sound_handle_pistons) end + end, + + on_activate = function(self, staticdata, dtime_s) + --core.chat_send_all('passengers: '.. dump(self._passengers)) + if staticdata ~= "" and staticdata ~= nil then + local data = core.deserialize(staticdata) or {} + + self._baloon_buoyancy = data.stored_baloon_buoyancy or 0 + self._energy = data.stored_energy or 0 + self._water_level = data.stored_water_level or 0 + self._boiler_pressure = data.stored_boiler_pressure or 0 + self.owner = data.stored_owner or "" + self._shared_owners = data.stored_shared_owners or {} + self.hp = 50 --data.stored_hp or 50 + self.color = data.stored_color or "blue" + self.color2 = data.stored_color2 or "white" + self.logo = data.stored_logo or "steampunk_blimp_alpha_logo.png" + self.anchored = data.stored_anchor or false + self.buoyancy = data.stored_buoyancy or 0.15 + self.hull_integrity = data.stored_hull_integrity + self.item = data.stored_item + self._passengers = data.stored_passengers or steampunk_blimp.copy_vector({[1]=nil, [2]=nil, [3]=nil, [4]=nil, [5]=nil, [6]=nil, [7]=nil}) + self._passengers_locked = data.stored_passengers_locked + self._ship_name = data.stored_ship_name + self._vehicle_name = data.stored_vehicle_name + self._remove = data.remove or false + if self._remove ~= true then + self._inv_id = data.stored_inv_id + end + --core.debug("loaded: ", self._energy) + local properties = self.object:get_properties() + properties.infotext = data.stored_owner .. " nice blimp" + self.object:set_properties(properties) + + if self._remove == true then + airutils.destroy_inventory(self) + self.object:remove() + return + end + end + + local colstr = steampunk_blimp.colors[self.color] + if not colstr then + colstr = "blue" + self.color = colstr + end + steampunk_blimp.paint(self, self.color) + steampunk_blimp.paint2(self, self.color2) + local pos = self.object:get_pos() + + if airutils.debug_log then + core.log("action","activating: "..self._vehicle_name.." from "..self.owner.." at position "..math.floor(pos.x)..","..math.floor(pos.y)..","..math.floor(pos.z)) + end + + local fire=core.add_entity(pos,'steampunk_blimp:fire') + fire:set_attach(self.object,'',{x=0.0,y=0.0,z=0.0},{x=0,y=0,z=0}) + self.fire = fire + + --passengers positions + self._passenger_is_sit = steampunk_blimp.copy_vector({}) + self._passengers_base = steampunk_blimp.copy_vector({}) + self._passengers_base_pos = steampunk_blimp.copy_vector({}) + for i = 1,steampunk_blimp.max_seats,1 + do + self._passenger_is_sit[i] = 0 + self._passengers_base_pos[i] = steampunk_blimp.copy_vector(steampunk_blimp.passenger_pos[i]) + self._passengers_base[i]=core.add_entity(pos,'steampunk_blimp:stand_base') + self._passengers_base[i]:set_attach(self.object,'',self._passengers_base_pos[i],{x=0,y=0,z=0}) + end + + --animation load - stoped + self.object:set_animation({x = 1, y = 47}, 0, 0, true) + + self.object:set_bone_position("low_rudder_a", {x=0,y=0,z=-40}, {x=-5.35,y=0,z=0}) + + self.object:set_armor_groups({immortal=1}) + + airutils.actfunc(self, staticdata, dtime_s) + + self.object:set_armor_groups({immortal=1}) + + if self._remove ~= true then + local inv = core.get_inventory({type = "detached", name = self._inv_id}) + -- if the game was closed the inventories have to be made anew, instead of just reattached + if not inv then + airutils.create_inventory(self, steampunk_blimp.trunk_slots) + else + self.inv = inv + end + end + + steampunk_blimp.engine_step(self, 0) + end, + + on_step = function(self,dtime,colinfo) + self.dtime = math.min(dtime,0.2) + self.colinfo = colinfo + self.height = airutils.get_box_height(self) + + -- physics comes first + local vel = self.object:get_velocity() + + if colinfo then + self.isonground = colinfo.touching_ground + else + if self.lastvelocity.y==0 and vel.y==0 then + self.isonground = true + else + self.isonground = false + end + end + + self:physics() + + if self.logic then + self:logic() + end + + self.lastvelocity = self.object:get_velocity() + self.time_total=self.time_total+self.dtime + end, + logic = function(self) + + local accel_y = self.object:get_acceleration().y + local rotation = self.object:get_rotation() + local yaw = rotation.y + local curr_pos = self.object:get_pos() + local newyaw + local newpitch + + local hull_direction = core.yaw_to_dir(yaw) + local nhdir = {x=hull_direction.z,y=0,z=-hull_direction.x} -- lateral unit vector + local velocity = self.object:get_velocity() + local wind_speed = airutils.get_wind(curr_pos, 0.15) + + local longit_speed = steampunk_blimp.dot(velocity,hull_direction) + self._longit_speed = longit_speed --for anchor verify + local relative_longit_speed = longit_speed + if steampunk_blimp.wind_enabled then + relative_longit_speed = steampunk_blimp.dot(vector.add(velocity, wind_speed), hull_direction) + end + self._relative_longit_speed = relative_longit_speed + + local longit_drag = vector.multiply(hull_direction,relative_longit_speed* + relative_longit_speed*LONGIT_DRAG_FACTOR*-1*steampunk_blimp.sign(relative_longit_speed)) + local later_speed = steampunk_blimp.dot(velocity,nhdir) + local later_drag = vector.multiply(nhdir,later_speed*later_speed* + LATER_DRAG_FACTOR*-1*steampunk_blimp.sign(later_speed)) + local accel = vector.add(longit_drag,later_drag) + + self._last_pos = curr_pos + self.object:move_to(curr_pos) + + if self.owner == "" then return end + + --fire + if self.fire then + if self._engine_running == true then + self.fire:set_properties({textures={steampunk_blimp.fire_tex},glow=15}) + else + self.fire:set_properties({textures={"steampunk_blimp_alpha.png"},glow=0}) + end + end + + --detect collision + steampunk_blimp.testDamage(self, velocity, curr_pos) + + accel = steampunk_blimp.control(self, self.dtime, hull_direction, relative_longit_speed, accel) or velocity + + --get disconnected players + steampunk_blimp.rescueConnectionFailedPassengers(self) + + local turn_rate = math.rad(18) + newyaw = yaw + self.dtime*(1 - 1 / (math.abs(relative_longit_speed) + 1)) * + self._rudder_angle / 30 * turn_rate * steampunk_blimp.sign(relative_longit_speed) + + steampunk_blimp.engine_step(self, accel) + + --roll adjust + --------------------------------- + local sdir = core.yaw_to_dir(newyaw) + local snormal = {x=sdir.z,y=0,z=-sdir.x} -- rightside, dot is negative + local prsr = steampunk_blimp.dot(snormal,nhdir) + local rollfactor = -15 + local newroll = 0 + if self._last_roll ~= nil then newroll = self._last_roll end + --oscilation when stoped + if relative_longit_speed == 0 then + local time_correction = (self.dtime/steampunk_blimp.ideal_step) + --stoped + if self._roll_state == nil then + self._roll_state = math.floor(math.random(-1,1)) + if self._roll_state == 0 then self._roll_state = 1 end + self._last_roll = newroll + end + if math.deg(newroll) >= 1 and self._roll_state == 1 then + self._roll_state = -1 + steampunk_blimp.play_rope_sound(self); + end + if math.deg(newroll) <= -1 and self._roll_state == -1 then + self._roll_state = 1 + steampunk_blimp.play_rope_sound(self); + end + local roll_factor = (self._roll_state * 0.005) * time_correction + self._last_roll = self._last_roll + math.rad(roll_factor) + else + --in movement + self._roll_state = nil + newroll = (prsr*math.rad(rollfactor))*later_speed + if self._last_roll ~= nil then + if math.sign(newroll) ~= math.sign(self._last_roll) then + steampunk_blimp.play_rope_sound(self) + end + end + self._last_roll = newroll + end + --core.chat_send_all('newroll: '.. newroll) + --------------------------------- + -- end roll + + if steampunk_blimp.wind_enabled then + --local wind_yaw = core.dir_to_yaw(wind_speed) + --core.chat_send_all("x: "..wind_speed.x.. " - z: "..wind_speed.z.." - yaw: "..math.deg(wind_yaw).. " - orig: "..wind_yaw) + + if self.anchored == false and self.isonground == false then + accel = vector.add(accel, wind_speed) + else + accel = vector.new() + end + end + accel.y = accel_y + + newpitch = velocity.y * math.rad(1.5) * (relative_longit_speed/3) + --self.object:set_acceleration(accel) + self.object:add_velocity(vector.multiply(accel,self.dtime)) + self.object:set_rotation({x=newpitch,y=newyaw,z=newroll}) + + local compass_angle = newyaw + local rem_obj = self.object:get_attach() + if rem_obj then + compass_angle = rem_obj:get_rotation().y + end + + self.object:set_bone_position("low_rudder", {x=0,y=0,z=0}, {x=0,y=self._rudder_angle,z=0}) + self.object:set_bone_position("rudder", {x=0,y=97,z=-148}, {x=0,y=self._rudder_angle,z=0}) + self.object:set_bone_position("timao", {x=0,y=27,z=-25}, {x=0,y=0,z=self._rudder_angle*8}) + self.object:set_bone_position("compass_axis", {x=0,y=30.2,z=-21.243}, {x=0, y=(math.deg(compass_angle)), z=0}) + + --saves last velocy for collision detection (abrupt stop) + self._last_vel = self.object:get_velocity() + self._last_accell = accel + + steampunk_blimp.move_persons(self) + end, + + on_punch = function(self, puncher, ttime, toolcaps, dir, damage) + if not puncher or not puncher:is_player() then + return + end + local is_admin + is_admin = core.check_player_privs(puncher, {server=true}) + local name = puncher:get_player_name() + if self.owner == nil then + self.owner = name + end + + local is_attached = steampunk_blimp.checkAttach(self, puncher) + + local itmstck=puncher:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + --core.chat_send_all(item_name) + + if is_attached == true then + --refuel + if steampunk_blimp.load_fuel(self, puncher) then return end + if steampunk_blimp.load_water(self, puncher) then return end + end + + if self.owner and self.owner ~= name and self.owner ~= "" then + if is_admin == false then return end + end + + if self.driver_name and self.driver_name ~= name then + -- do not allow other players to remove the object while there is a driver + return + end + + -- deal with painting or destroying + if itmstck then + --core.chat_send_all(dump(item_name)) + local find_str = 'dye:' + if airutils.is_mcl and not core.get_modpath("mcl_playerplus") then + --mineclonia + find_str = 'mcl_dyes:' + end + local _,indx = item_name:find(find_str) + if indx then + + --lets paint!!!! + local color = nil + if not airutils.is_repixture then + color = item_name:sub(indx+1) + end + local colstr = steampunk_blimp.colors[color] + --core.chat_send_all(color ..' '.. dump(colstr)) + if colstr and (name == self.owner or core.check_player_privs(puncher, {protection_bypass=true})) then + local ctrl = puncher:get_player_control() + if ctrl.aux1 then + steampunk_blimp.paint2(self, colstr) + else + steampunk_blimp.paint(self, colstr) + end + itmstck:set_count(itmstck:get_count()-1) + puncher:set_wielded_item(itmstck) + end + -- end painting + end + end + + if is_attached == false then + local has_passengers = false + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers[i] ~= nil then + has_passengers = true + break + end + end + + + if not has_passengers and toolcaps and toolcaps.damage_groups and + toolcaps.groupcaps and (toolcaps.groupcaps.choppy or toolcaps.groupcaps.axey_dig) then + + local is_empty = true + + --airutils.make_sound(self,'hit') + if is_empty == true then + self.hp = self.hp - 10 + core.sound_play("steampunk_blimp_collision", { + object = self.object, + max_hear_distance = 5, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }) + end + end + + if self.hp <= 0 then + steampunk_blimp.destroy(self, false) + end + + end + + end, + + on_rightclick = function(self, clicker) + if not clicker or not clicker:is_player() then + return + end + + local name = clicker:get_player_name() + + if self.owner == "" then + self.owner = name + end + + --core.chat_send_all('passengers: '.. dump(self._passengers)) + --========================= + -- form to pilot + --========================= + local is_attached = false + local seat = clicker:get_attach() + if seat then + local plane = seat:get_attach() + if plane == self.object then is_attached = true end + end + + --check error after being shot for any other mod + if is_attached == false then + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers[i] == name then + self._passengers[i] = nil --clear the wrong information + break + end + end + end + + --shows pilot formspec + if name == self.driver_name then + if is_attached then + steampunk_blimp.pilot_formspec(name) + else + self.driver_name = nil + end + --========================= + -- attach passenger + --========================= + else + local pass_is_attached = steampunk_blimp.check_passenger_is_attached(self, name) + + if pass_is_attached then + local can_bypass = core.check_player_privs(clicker, {protection_bypass=true}) + if clicker:get_player_control().aux1 == true then --lets see the inventory + local is_shared = false + if name == self.owner or can_bypass then is_shared = true end + for k, v in pairs(self._shared_owners) do + if v == name then + is_shared = true + break + end + end + if is_shared then + airutils.show_vehicle_trunk_formspec(self, clicker, steampunk_blimp.trunk_slots) + end + else + if self.driver_name ~= nil and self.driver_name ~= "" then + --lets take the control by force + if name == self.owner or can_bypass then + --require the pilot position now + steampunk_blimp.owner_formspec(name) + else + steampunk_blimp.pax_formspec(name) + end + else + --check if is on owner list + local is_shared = false + if name == self.owner or can_bypass then is_shared = true end + for k, v in pairs(self._shared_owners) do + if v == name then + is_shared = true + break + end + end + --normal user + if is_shared == false then + steampunk_blimp.pax_formspec(name) + else + --owners + steampunk_blimp.pilot_formspec(name) + end + end + end + else + --first lets clean the boat slots + --note that when it happens, the "rescue" function will lost the historic + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers[i] ~= nil then + local old_player = core.get_player_by_name(self._passengers[i]) + if not old_player then self._passengers[i] = nil end + end + end + --attach normal passenger + --if self._door_closed == false then + steampunk_blimp.attach_pax(self, clicker) + --end + end + end + + end, + + on_deactivate = airutils.on_deactivate, +}) diff --git a/mods/steampunk_blimp/forms.lua b/mods/steampunk_blimp/forms.lua new file mode 100644 index 00000000..d5687987 --- /dev/null +++ b/mods/steampunk_blimp/forms.lua @@ -0,0 +1,623 @@ +function steampunk_blimp.getPlaneFromPlayer(player) + local seat = player:get_attach() + if seat then + local plane = seat:get_attach() + return plane + end + return nil +end + +function steampunk_blimp.pilot_formspec(name) + local basic_form = table.concat({ + "formspec_version[5]", + "size[6,9]", + }, "") + + local player = minetest.get_player_by_name(name) + local plane_obj = steampunk_blimp.getPlaneFromPlayer(player) + if plane_obj == nil then + return + end + local ent = plane_obj:get_luaentity() + + local take_control = "false" + if ent._at_control then take_control = "true" end + local anchor = "false" + if ent.anchored == true then anchor = "true" end + + basic_form = basic_form.."button[1,1.0;4,1;turn_on;Start/Stop the fire]" + basic_form = basic_form.."button[1,2.0;4,1;water;Load water from below]" + if ent._remove ~= true then + basic_form = basic_form.."button[1,3.0;4,1;inventory;Open inventory]" + end + basic_form = basic_form.."button[1,4.0;4,1;manual;Show Manual Menu]" + + basic_form = basic_form.."checkbox[1,5.6;take_control;Take the Control;"..take_control.."]" + basic_form = basic_form.."checkbox[1,6.2;anchor;Anchor away;"..anchor.."]" + + basic_form = basic_form.."label[1,7.0;Disembark:]" + basic_form = basic_form.."button[1,7.2;2,1;disembark_l;<< Left]" + basic_form = basic_form.."button[3,7.2;2,1;disembark_r;Right >>]" + + minetest.show_formspec(name, "steampunk_blimp:pilot_main", basic_form) +end + +function steampunk_blimp.pax_formspec(name) + local basic_form = table.concat({ + "formspec_version[3]", + "size[6,3]", + }, "") + + basic_form = basic_form.."label[1,1.0;Disembark:]" + basic_form = basic_form.."button[1,1.2;2,1;disembark_l;<< Left]" + basic_form = basic_form.."button[3,1.2;2,1;disembark_r;Right >>]" + + minetest.show_formspec(name, "steampunk_blimp:passenger_main", basic_form) +end + +local default_logos = { + "blimp_clover.png", + "blimp_liz.png", + "blimp_shotting_star.png", + "blimp_skull.png", + "blimp_jack.png", + "blimp_xmas.png", +} +function steampunk_blimp.logo_ext_formspec(name, t_index, t_page, t_type) + t_index = t_index or 1 + t_page = t_page or 1 + t_type = t_type or 1 + + if airutils.isTextureLoaded then + airutils.isTextureLoaded('heart.png') --force the textures first load + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are using an old version of airutils, update it first")) + return + end + + local basic_form = table.concat({ + "formspec_version[4]", + "size[12,9]", + }, "") + + local textures = {} + if t_type == "1" or t_type == 1 then textures = airutils.properties_copy(default_logos) end + if t_type == "2" or t_type == 2 then textures = airutils.properties_copy(airutils.all_game_textures) end + if t_type == "3" or t_type == 3 then textures = airutils.properties_copy(airutils.all_entities_textures) end + if t_type == "4" or t_type == 4 then textures = airutils.properties_copy(airutils.all_items_textures) end + + local text_count = #textures + local items_per_page = 50 + local pages = math.ceil(text_count / items_per_page) + local logolist = "" + local items_count = 0 + local item_start = ((t_page-1)*items_per_page) + 1 + for k, v in pairs(textures) do + if k >= item_start and items_count < items_per_page then + logolist = logolist .. v .. "," + items_count = items_count + 1 + end + if items_count >= items_per_page then break end + end + + local pages_list = "" + for i = 1,pages,1 + do + pages_list = pages_list .. i .. "," + end + + basic_form = basic_form.."label[0.5,0.9;Type]" + basic_form = basic_form.."dropdown[2,0.5;3,0.8;t_type;Default,Nodes,Entities,Items;"..t_type..";true]" + basic_form = basic_form.."textlist[0.5,1.5;4.5,6;logos;"..logolist..";"..t_index..";false]" + local curr_real_index = (items_per_page * (t_page-1)) + t_index + local texture_name = textures[curr_real_index] or "" + basic_form = basic_form.."image[5.5,1.5;6,6;"..texture_name.."]" + basic_form = basic_form.."label[0.6,8.2;Page]" + basic_form = basic_form.."dropdown[1.8,7.8;1.9,0.8;t_page;"..pages_list..";"..t_page..";true]" + basic_form = basic_form.."button[8.5,7.8;3,0.8;set_texture;Set Texture]" + + basic_form = basic_form.."field[5.3,20.0;3,0.8;texture_name;;"..texture_name.."]" + basic_form = basic_form.."field[5.3,21.0;3,0.8;last_type;;"..t_type.."]" + + minetest.show_formspec(name, "steampunk_blimp:logo_ext", basic_form) +end + +function steampunk_blimp.owner_formspec(name) + local basic_form = table.concat({ + "formspec_version[3]", + "size[6,4.2]", + }, "") + + basic_form = basic_form.."button[1,1.0;4,1;take;Take the Control Now]" + basic_form = basic_form.."label[1,2.2;Disembark:]" + basic_form = basic_form.."button[1,2.4;2,1;disembark_l;<< Left]" + basic_form = basic_form.."button[3,2.4;2,1;disembark_r;Right >>]" + + minetest.show_formspec(name, "steampunk_blimp:owner_main", basic_form) +end + +function set_list(list) + local set = {} + for _, l in ipairs(list) do set[l] = true end + return set +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "steampunk_blimp:owner_main" then + local name = player:get_player_name() + local plane_obj = steampunk_blimp.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "steampunk_blimp:owner_main") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.disembark_l then + steampunk_blimp.dettach_pax(ent, player, "l") + end + if fields.disembark_r then + steampunk_blimp.dettach_pax(ent, player, "r") + end + if fields.take then + ent._at_control = true + for i = 5,1,-1 + do + if ent._passengers[i] == name then + ent._passengers_base_pos[i] = vector.new(steampunk_blimp.pilot_base_pos) + ent._passengers_base[i]:set_attach(ent.object,'',steampunk_blimp.pilot_base_pos,{x=0,y=0,z=0}) + player:set_attach(ent._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + end + if ent._passengers[i] == ent.driver_name then + ent._passengers_base_pos[i] = vector.new(steampunk_blimp.passenger_pos[i]) + ent._passengers_base[i]:set_attach(ent.object,'',ent._passengers_base_pos[i],{x=0,y=0,z=0}) + end + end + ent.driver_name = name + end + end + minetest.close_formspec(name, "steampunk_blimp:owner_main") + end + if formname == "steampunk_blimp:passenger_main" then + local name = player:get_player_name() + local plane_obj = steampunk_blimp.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "steampunk_blimp:passenger_main") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.disembark_l then + steampunk_blimp.dettach_pax(ent, player, "l") + end + if fields.disembark_r then + steampunk_blimp.dettach_pax(ent, player, "r") + end + end + minetest.close_formspec(name, "steampunk_blimp:passenger_main") + end + if formname == "steampunk_blimp:logo_ext" then + local name = player:get_player_name() + local plane_obj = steampunk_blimp.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "steampunk_blimp:logo_ext") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.set_texture then + if ent.name == "steampunk_blimp:blimp" then + if ent.owner == name or minetest.check_player_privs(name, {protection_bypass=true}) then + if fields.texture_name then + local image_name = fields.texture_name + local logo_list = set_list(default_logos) + if airutils.isTextureLoaded(image_name) or logo_list[image_name] then + steampunk_blimp.set_logo(ent, image_name) + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> texture '"..image_name.."' set")) + --minetest.close_formspec(name, "steampunk_blimp:logo_ext") + return + end + end + end + end + end + if fields.logos or fields.t_page then + --minetest.close_formspec(name, "steampunk_blimp:logo_ext") + --steampunk_blimp.logo_ext_formspec(name,fields.logos) + local result = minetest.explode_textlist_event(fields.logos) + if result.type == "CHG" then + --minetest.chat_send_all(dump(result.index)) + --minetest.close_formspec(name, "steampunk_blimp:logo_ext") + steampunk_blimp.logo_ext_formspec(name,result.index,fields.t_page,fields.last_type) + return + end + steampunk_blimp.logo_ext_formspec(name,1,fields.t_page,fields.last_type) + return + end + if fields.t_type then + steampunk_blimp.logo_ext_formspec(name,1,1,fields.t_type) + return + end + end + end + if formname == "steampunk_blimp:pilot_main" then + local name = player:get_player_name() + local plane_obj = steampunk_blimp.getPlaneFromPlayer(player) + if plane_obj == nil then + minetest.close_formspec(name, "steampunk_blimp:pilot_main") + return + end + local ent = plane_obj:get_luaentity() + if ent then + if fields.turn_on then + steampunk_blimp.start_furnace(ent) + end + if fields.water then + if ent.isinliquid then + if ent._engine_running == true then + steampunk_blimp.start_furnace(ent) + end + if ent._boiler_pressure > 0 then + minetest.sound_play({name = "default_cool_lava"}, + {object = ent.object, gain = 1.0, + pitch = 1.0, + max_hear_distance = 32, + loop = false,}, true) + end + ent._boiler_pressure = 0 + ent._water_level = steampunk_blimp.MAX_WATER + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> Impossible. The ship needs to be in the water.")) + end + end + if fields.inventory then + if ent._remove ~= true then + airutils.show_vehicle_trunk_formspec(ent, player, steampunk_blimp.trunk_slots) + end + end + if fields.manual then + steampunk_blimp.manual_formspec(name) + end + if fields.take_control then + if fields.take_control == "true" then + if ent.driver_name == nil or ent.driver_name == "" then + ent._at_control = true + for i = steampunk_blimp.max_seats,1,-1 + do + if ent._passengers[i] == name then + ent._passengers_base_pos[i] = vector.new(steampunk_blimp.pilot_base_pos) + ent._passengers_base[i]:set_attach(ent.object,'',steampunk_blimp.pilot_base_pos,{x=0,y=0,z=0}) + player:set_attach(ent._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + ent.driver_name = name + --minetest.chat_send_all(">>"..ent.driver_name) + break + end + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> Impossible. Someone is at the blimp control now.")) + end + else + ent.driver_name = nil + ent._at_control = false + steampunk_blimp.remove_hud(player) + end + end + if fields.disembark_l then + --========================= + -- dettach player + --========================= + -- eject passenger if the plane is on ground + + steampunk_blimp.dettach_pax(ent, player, "l") + + end + if fields.disembark_r then + --========================= + -- dettach player + --========================= + -- eject passenger if the plane is on ground + + steampunk_blimp.dettach_pax(ent, player, "r") + + end + if fields.anchor then + if fields.anchor == "true" then + local max_speed_anchor = 0.6 + if ent._longit_speed then + if math.abs(ent._longit_speed) < max_speed_anchor then + + ent.anchored = true + ent.object:set_acceleration(vector.new()) + ent.object:set_velocity(vector.new()) + if name then + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> Anchor away!")) + end + --ent.buoyancy = 0.1 + else + if name then + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> Too fast to set anchor!")) + end + end + end + else + ent.anchored = false + if name then + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> Weigh anchor!")) + end + end + --ent._rudder_angle = 0 + end + end + minetest.close_formspec(name, "steampunk_blimp:pilot_main") + end +end) + + +minetest.register_chatcommand("blimp_share", { + params = "name", + description = "Share ownewrship with your friends", + privs = {interact = true}, + func = function(name, param) + local player = minetest.get_player_by_name(name) + local target_player = minetest.get_player_by_name(param) + local attached_to = player:get_attach() + + if attached_to ~= nil and target_player ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + if entity.owner == name then + local exists = false + for k, v in pairs(entity._shared_owners) do + if v == param then + exists = true + break + end + end + if exists == false then + table.insert(entity._shared_owners, param) + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> blimp shared")) + --minetest.chat_send_all(dump(entity._shared_owners)) + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> this user is already registered for blimp share")) + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> only the owner can share this blimp")) + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end +}) + +minetest.register_chatcommand("blimp_remove", { + params = "name", + description = "Removes ownewrship from someone", + privs = {interact = true}, + func = function(name, param) + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + if entity.owner == name then + for k, v in pairs(entity._shared_owners) do + if v == param then + table.remove(entity._shared_owners,k) + break + end + end + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> user removed")) + --minetest.chat_send_all(dump(entity._shared_owners)) + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> only the owner can do this action")) + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end +}) + +minetest.register_chatcommand("blimp_list", { + params = "", + description = "Lists the blimp shared owners", + privs = {interact = true}, + func = function(name, param) + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + minetest.chat_send_player(name,core.colorize('#ffff00', " >>> Current owners are:")) + minetest.chat_send_player(name,core.colorize('#0000ff', entity.owner)) + for k, v in pairs(entity._shared_owners) do + minetest.chat_send_player(name,core.colorize('#00ff00', v)) + end + --minetest.chat_send_all(dump(entity._shared_owners)) + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end +}) + +minetest.register_chatcommand("blimp_lock", { + params = "true/false", + description = "Blocks boarding of non-owners. true to lock, false to unlock", + privs = {interact = true}, + func = function(name, param) + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + if param == "true" then + entity._passengers_locked = true + minetest.chat_send_player(name,core.colorize('#ffff00', " >>> Non owners cannot enter now.")) + elseif param == "false" then + entity._passengers_locked = false + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> Non owners are free to enter now.")) + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are not inside a blimp to perform this command")) + end + end +}) + +minetest.register_chatcommand("blimp_logo", { + params = "", + description = "Changes blimp logo", + privs = {interact = true}, + func = function(name, param) + local image_name = param --"blimp_alpha.png^"..param + local colorstring = core.colorize('#ff0000', " >>> you are not inside a blimp") + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + if entity.owner == name or minetest.check_player_privs(name, {protection_bypass=true}) then + if airutils.isTextureLoaded then + if param == '' then + steampunk_blimp.logo_ext_formspec(name) + else + local logo_list = set_list(default_logos) + if airutils.isTextureLoaded(image_name) or logo_list[image_name] then + steampunk_blimp.set_logo(entity, image_name) + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> texture '"..image_name.."' set")) + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> texture '"..image_name.."' not found")) + end + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> you are using an old version of airutils, update it first")) + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> only the owner can do this action")) + end + else + minetest.chat_send_player(name,colorstring) + end + end + end + else + minetest.chat_send_player(name,colorstring) + end + end +}) + +minetest.register_chatcommand("blimp_eject", { + params = "", + description = "Ejects from the blimp - useful for clients before 5.3", + privs = {interact = true}, + func = function(name, param) + local colorstring = core.colorize('#ff0000', " >>> you are not inside a blimp") + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + for i = steampunk_blimp.max_seats,1,-1 + do + if entity._passengers[i] == name then + steampunk_blimp.dettach_pax(entity, player, "l") + break + end + end + else + minetest.chat_send_player(name,colorstring) + end + end + end + else + minetest.chat_send_player(name,colorstring) + end + end +}) + +if airutils.is_repixture then + local available_text = "The available colors are: black, blue, brown, cyan, dark_green, dark_grey, green, grey, magenta, orange, pink, red, violet, white or yellow" + minetest.register_chatcommand("blimp_paint", { + params = " ", + description = "Paints the blimp with a primary and secondary colors. "..available_text, + privs = {interact = true}, + func = function(name, param) + local colorstring = core.colorize('#ff0000', " >>> you are not inside a blimp") + local player = minetest.get_player_by_name(name) + local attached_to = player:get_attach() + + if attached_to ~= nil then + local seat = attached_to:get_attach() + if seat ~= nil then + local entity = seat:get_luaentity() + if entity then + if entity.name == "steampunk_blimp:blimp" then + if entity.owner == name or minetest.check_player_privs(name, {protection_bypass=true}) then + --lets paint!!!! + local color1, color2 = param:match("^([%a%d_-]+) (.+)$") + + --minetest.chat_send_all(dump(color1).." - "..dump(color2)) + local colstr = steampunk_blimp.colors[color1] + local colstr2 = steampunk_blimp.colors[color2 or "white"] + --minetest.chat_send_all(color ..' '.. dump(colstr)) + if colstr and colstr2 then + steampunk_blimp.paint2(entity, colstr) + steampunk_blimp.paint(entity, colstr2) + minetest.chat_send_player(name,core.colorize('#00ff00', " >>> colors set successfully")) + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> some of the colors wasn't specified correctly. "..available_text)) + end + else + minetest.chat_send_player(name,core.colorize('#ff0000', " >>> only the owner can do this action")) + end + else + minetest.chat_send_player(name,colorstring) + end + end + end + else + minetest.chat_send_player(name,colorstring) + end + end + }) +end diff --git a/mods/steampunk_blimp/fuel_management.lua b/mods/steampunk_blimp/fuel_management.lua new file mode 100644 index 00000000..bbca737e --- /dev/null +++ b/mods/steampunk_blimp/fuel_management.lua @@ -0,0 +1,97 @@ +-- +-- fuel +-- +steampunk_blimp.MAX_FUEL = minetest.settings:get("steampunk_blimp_max_fuel") or 99 +steampunk_blimp.FUEL_CONSUMPTION = minetest.settings:get("steampunk_blimp_fuel_consumption") or 6000 + +steampunk_blimp.MAX_WATER = 10 +steampunk_blimp.WATER_CONSUMPTION = 50000 + +function steampunk_blimp.contains(table, val) + for k,v in pairs(table) do + if k == val then + return v + end + end + return false +end + +function steampunk_blimp.load_fuel(self, player) + local inv = player:get_inventory() + + local itmstck=player:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + local grp_wood = minetest.get_item_group(item_name, "wood") + local grp_tree = minetest.get_item_group(item_name, "tree") + if grp_wood == 1 or grp_tree == 1 then + local stack = ItemStack(item_name .. " 1") + + if self._energy < steampunk_blimp.MAX_FUEL then + inv:remove_item("main", stack) + local amount = 1 + if grp_tree == 1 then amount = 4 end + self._energy = self._energy + amount + if self._energy > steampunk_blimp.MAX_FUEL then self._energy = steampunk_blimp.MAX_FUEL end + end + return true + end + + --minetest.chat_send_all("fuel: ".. dump(item_name)) + local fuel = steampunk_blimp.contains(steampunk_blimp.fuel, item_name) + if fuel then + local stack = ItemStack(item_name .. " 1") + + if self._energy < steampunk_blimp.MAX_FUEL then + inv:remove_item("main", stack) + self._energy = self._energy + fuel.amount + if self._energy > steampunk_blimp.MAX_FUEL then self._energy = steampunk_blimp.MAX_FUEL end + --minetest.chat_send_all(self.energy) + + --local energy_indicator_angle = steampunk_blimp.get_pointer_angle(self._energy, steampunk_blimp.MAX_FUEL) + end + + return true + end + + return false +end + +function steampunk_blimp.load_water(self, player) + --local inv = player:get_inventory() + + local itmstck=player:get_wielded_item() + local item_name = "" + if itmstck then item_name = itmstck:get_name() end + + --minetest.chat_send_all("water: ".. dump(item_name)) + local water = steampunk_blimp.contains(steampunk_blimp.water, item_name) + if water then + if self._water_level < steampunk_blimp.MAX_WATER then + local itemstack + --itmstck:set_count(1) + --inv:remove_item("main", itmstck) + + local bucket_name = 'bucket:bucket' + if airutils.is_mcl then bucket_name = "mcl_buckets:bucket" end + if airutils.is_repixture then bucket_name = "rp_default:bucket_" end + local indx = item_name:find(bucket_name) + if indx then + itemstack = ItemStack(bucket_name.."_empty") + --inv:add_item("main", itemstack) + player:set_wielded_item(itemstack) + end + if not itemstack then + itemstack = ItemStack(item_name .. " 1") + end + + self._water_level = self._water_level + water.amount + if self._water_level > steampunk_blimp.MAX_WATER then self._water_level = steampunk_blimp.MAX_WATER end + end + + return true + end + + return false +end diff --git a/mods/steampunk_blimp/hud.lua b/mods/steampunk_blimp/hud.lua new file mode 100644 index 00000000..319d37ab --- /dev/null +++ b/mods/steampunk_blimp/hud.lua @@ -0,0 +1,253 @@ +steampunk_blimp.hud_list = {} + +function steampunk_blimp.get_pointer_angle(value, maxvalue) + local angle = value/maxvalue * 180 + --angle = angle - 90 + --angle = angle * -1 + return angle +end + +function steampunk_blimp.animate_gauge(player, ids, prefix, x, y, angle) + local angle_in_rad = math.rad(angle + 180) + local dim = 10 + local pos_x = math.sin(angle_in_rad) * dim + local pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "2"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 20 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "3"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 30 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "4"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 40 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "5"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 50 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "6"], "offset", {x = pos_x + x, y = pos_y + y}) + dim = 60 + pos_x = math.sin(angle_in_rad) * dim + pos_y = math.cos(angle_in_rad) * dim + player:hud_change(ids[prefix .. "7"], "offset", {x = pos_x + x, y = pos_y + y}) +end + +function steampunk_blimp.update_hud(player, coal, water, pressure, power_lever) + if player == nil then return end + local player_name = player:get_player_name() + + local screen_pos_y = -100 + local screen_pos_x = 10 + + local water_gauge_x = screen_pos_x + 374 + local water_gauge_y = screen_pos_y + local press_gauge_x = screen_pos_x + 85 + local press_gauge_y = water_gauge_y + local coal_1_x = screen_pos_x + 182 + local coal_1_y = screen_pos_y + local coal_2_x = coal_1_x + 60 + local coal_2_y = screen_pos_y + local throttle_x = screen_pos_x + 395 + local throttle_y = screen_pos_y + 45 + + local ids = steampunk_blimp.hud_list[player_name] + if ids then + local coal_value = coal + if coal_value > 99 then coal_value = 99 end + if coal_value < 0 then coal_value = 0 end + player:hud_change(ids["coal_1"], "text", "steampunk_blimp_"..(math.floor(coal_value/10))..".png") + player:hud_change(ids["coal_2"], "text", "steampunk_blimp_"..(math.floor(coal_value%10))..".png") + + player:hud_change(ids["throttle"], "offset", {x = throttle_x, y = throttle_y - power_lever}) + + steampunk_blimp.animate_gauge(player, ids, "water_pt_", water_gauge_x, water_gauge_y, water) + steampunk_blimp.animate_gauge(player, ids, "press_pt_", press_gauge_x, press_gauge_y, pressure) + else + ids = {} + + ids["title"] = player:hud_add({ + hud_elem_type = "text", + position = {x = 0, y = 1}, + offset = {x = screen_pos_x + 240, y = screen_pos_y - 100}, + text = "Blimp engine state", + alignment = 0, + scale = { x = 100, y = 30}, + number = 0xFFFFFF, + }) + + ids["bg"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = screen_pos_x, y = screen_pos_y}, + text = "steampunk_blimp_hud_panel.png", + scale = { x = 0.5, y = 0.5}, + alignment = { x = 1, y = 0 }, + }) + + ids["coal_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = coal_1_x, y = coal_1_y}, + text = "steampunk_blimp_0.png", + scale = { x = 0.5, y = 0.5}, + alignment = { x = 1, y = 0 }, + }) + + ids["coal_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = coal_2_x, y = coal_2_y}, + text = "steampunk_blimp_0.png", + scale = { x = 0.5, y = 0.5}, + alignment = { x = 1, y = 0 }, + }) + + ids["throttle"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = throttle_x, y = throttle_y}, + text = "steampunk_blimp_throttle.png", + scale = { x = 0.5, y = 0.5}, + alignment = { x = 1, y = 0 }, + }) + + ids["water_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["water_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["water_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["water_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["water_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["water_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["water_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = water_gauge_x, y = water_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + ids["press_pt_1"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["press_pt_2"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["press_pt_3"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["press_pt_4"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["press_pt_5"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["press_pt_6"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + ids["press_pt_7"] = player:hud_add({ + hud_elem_type = "image", + position = {x = 0, y = 1}, + offset = {x = press_gauge_x, y = press_gauge_y}, + text = "steampunk_blimp_ind_box.png", + scale = { x = 6, y = 6}, + alignment = { x = 1, y = 0 }, + }) + + steampunk_blimp.hud_list[player_name] = ids + end +end + + +function steampunk_blimp.remove_hud(player) + if player then + local player_name = player:get_player_name() + --minetest.chat_send_all(player_name) + local ids = steampunk_blimp.hud_list[player_name] + --minetest.chat_send_all(dump(ids)) + if ids then + --player:hud_remove(ids["altitude"]) + --player:hud_remove(ids["time"]) + for key in pairs(ids) do + player:hud_remove(ids[key]) + end + steampunk_blimp.hud_list[player_name] = nil + end + end + +end diff --git a/mods/steampunk_blimp/init.lua b/mods/steampunk_blimp/init.lua new file mode 100644 index 00000000..a5349936 --- /dev/null +++ b/mods/steampunk_blimp/init.lua @@ -0,0 +1,459 @@ +steampunk_blimp={} +steampunk_blimp.gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.8 +steampunk_blimp.trunk_slots = 50 +steampunk_blimp.fuel = {['default:coal_lump'] = {amount=1},['default:coalblock'] = {amount=10}, ['rp_default:lump_coal'] = {amount=1}, ['rp_default:block_coal'] = {amount=10}, + ['mcl_core:coal_lump'] = {amount=1},['mcl_core:coalblock'] = {amount=10}, ['default:coal_lump'] = {amount=1}, ['default:coalblock'] = {amount=10}} +steampunk_blimp.water = {['default:water_source'] = {amount=1},['default:river_water_source'] = {amount=1}, + ['bucket:bucket_water'] = {amount=1}, ['bucket:bucket_river_water'] = {amount=1}, + ['mcl_buckets:bucket_water'] = {amount=1}, ['mcl_buckets:bucket_river_water'] = {amount=1}, ['mcl_core:water_source'] = {amount=1}, + ['rp_default:bucket_water'] = {amount=1}, ['rp_default:bucket_river_water'] = {amount=1}, } --bucket:bucket_empty +steampunk_blimp.ideal_step = 0.02 +steampunk_blimp.rudder_limit = 30 +steampunk_blimp.iddle_rotation = 0 +steampunk_blimp.max_engine_acc = 3 +steampunk_blimp.max_seats = 7 +steampunk_blimp.wind_enabled = false +steampunk_blimp.pilot_base_pos = {x=0.0,y=20.821,z=-30} +steampunk_blimp.passenger_pos = { + [1] = {x=0.0,y=0,z=-15}, + [2] = {x=-11,y=0,z=-12}, + [3] = {x=11,y=0,z=-12}, + [4] = {x=-11,y=0,z=14}, + [5] = {x=11,y=0,z=14}, + [6] = {x=-11,y=0,z=13}, + [7] = {x=11,y=0,z=13}, + } + +steampunk_blimp.furnace_sound = { name = "default_furnace_active", pitch = 1.0, gain = 0.2 } +steampunk_blimp.piston_sound = { name = "default_cool_lava", pitch = 0.4, gain = 0.05 } +steampunk_blimp.steps_sound = { name = "default_wood_footstep", pitch = 1.0, gain = 0.1 } + +if airutils.is_mcl then + steampunk_blimp.furnace_sound = { name = "fire_fire", pitch = 1.0, gain = 0.2 } +elseif airutils.is_repixture then + steampunk_blimp.furnace_sound = nil + steampunk_blimp.piston_sound = { name = "rp_default_torch_burnout", pitch = 0.3, gain = 0.15 } + steampunk_blimp.steps_sound = { name = "rp_sounds_footstep_wood", pitch = 1.0, gain = 0.5 } +end + +if airutils.is_repixture then + steampunk_blimp.color1_texture = "rp_default_reed_block_side.png" + steampunk_blimp.color2_texture = "rp_default_reed_block_top.png" + + steampunk_blimp.fire_tex = "[combine:16x16:0,0=steampunk_blimp_alpha.png:0,0=rp_fire_bonfire_flame.png" --"rp_fire_bonfire_flame.png^[resize:16x16" + steampunk_blimp.canvas_texture = "mobs_wool.png^[colorize:#f4e7c1:128" + steampunk_blimp.metal_texture = "default_sand.png^[colorize:#a3acac:128" + steampunk_blimp.black_texture = "default_sand.png^[colorize:#030303:200" + steampunk_blimp.wood_texture = "default_sand.png^[colorize:#3a270d:230" + steampunk_blimp.forno_texture = steampunk_blimp.black_texture.."^[mask:steampunk_blimp_forno_mask.png" + steampunk_blimp.rotor_texture = "("..steampunk_blimp.canvas_texture.."^[mask:steampunk_blimp_rotor_mask2.png)^(default_wood_oak.png^[mask:steampunk_blimp_rotor_mask.png)" + local ladder_texture = "default_ladder.png" + steampunk_blimp.textures = { + steampunk_blimp.black_texture, --alimentacao balao + "default_wood_oak.png", --asa + steampunk_blimp.canvas_texture, --asa + steampunk_blimp.canvas_texture, --balao + steampunk_blimp.color2_texture, --faixas brancas nariz + steampunk_blimp.color1_texture, --faixas azuis nariz + steampunk_blimp.metal_texture, --pontas do balão + "airutils_name_canvas.png", + steampunk_blimp.black_texture, --caldeira + steampunk_blimp.forno_texture, --caldeira + "default_wood_oak.png^[multiply:#A09090", --casco + steampunk_blimp.black_texture, -- corpo da bussola + steampunk_blimp.metal_texture, -- indicador bussola + steampunk_blimp.canvas_texture, --leme + "default_wood_oak.png^[multiply:#A09090", --leme + steampunk_blimp.wood_texture, --timao + "steampunk_blimp_compass.png", + ladder_texture, --escada + "default_wood_oak.png", --mureta + steampunk_blimp.wood_texture, --mureta + steampunk_blimp.black_texture, --nacele rotores + steampunk_blimp.wood_texture, --quilha + "default_wood_oak.png", --rotores + steampunk_blimp.rotor_texture, --"steampunk_blimp_rotor.png", --rotores + steampunk_blimp.black_texture, --suportes rotores + "default_wood_oak.png^[multiply:#A09090", --suporte timao + "steampunk_blimp_rope.png", --cordas + steampunk_blimp.color1_texture, --det azul + steampunk_blimp.color2_texture, --det branco + steampunk_blimp.wood_texture, --fixacao cordas + "steampunk_blimp_alpha_logo.png", --logo + } +else + steampunk_blimp.color1_texture = "wool_blue.png" + steampunk_blimp.color2_texture = "wool_yellow.png" + + steampunk_blimp.fire_tex = "default_furnace_fire_fg.png" + steampunk_blimp.canvas_texture = "wool_white.png^[colorize:#f4e7c1:128" + steampunk_blimp.metal_texture = "default_clay.png^[colorize:#a3acac:128" + steampunk_blimp.black_texture = "default_clay.png^[colorize:#030303:200" + steampunk_blimp.wood_texture = "default_clay.png^[colorize:#3a270d:230" + steampunk_blimp.forno_texture = steampunk_blimp.black_texture.."^[mask:steampunk_blimp_forno_mask.png" + steampunk_blimp.rotor_texture = "("..steampunk_blimp.canvas_texture.."^[mask:steampunk_blimp_rotor_mask2.png)^(default_wood.png^[mask:steampunk_blimp_rotor_mask.png)" + local ladder_texture = "default_ladder_wood.png" + if airutils.is_mcl then ladder_texture = "default_ladder.png" end + steampunk_blimp.textures = { + steampunk_blimp.black_texture, --alimentacao balao + "default_wood.png", --asa + steampunk_blimp.canvas_texture, --asa + steampunk_blimp.canvas_texture, --balao + steampunk_blimp.color2_texture, --faixas brancas nariz + steampunk_blimp.color1_texture, --faixas azuis nariz + steampunk_blimp.metal_texture, --pontas do balão + "airutils_name_canvas.png", + steampunk_blimp.black_texture, --caldeira + steampunk_blimp.forno_texture, --caldeira + "default_junglewood.png", --casco + steampunk_blimp.black_texture, -- corpo da bussola + steampunk_blimp.metal_texture, -- indicador bussola + steampunk_blimp.canvas_texture, --leme + "default_junglewood.png", --leme + steampunk_blimp.wood_texture, --timao + "steampunk_blimp_compass.png", + ladder_texture, --escada + "default_wood.png", --mureta + steampunk_blimp.wood_texture, --mureta + steampunk_blimp.black_texture, --nacele rotores + steampunk_blimp.wood_texture, --quilha + "default_wood.png", --rotores + steampunk_blimp.rotor_texture, --"steampunk_blimp_rotor.png", --rotores + steampunk_blimp.black_texture, --suportes rotores + "default_junglewood.png", --suporte timao + "steampunk_blimp_rope.png", --cordas + steampunk_blimp.color1_texture, --det azul + steampunk_blimp.color2_texture, --det branco + steampunk_blimp.wood_texture, --fixacao cordas + "steampunk_blimp_alpha_logo.png", --logo + --"steampunk_blimp_metal.png", + --"steampunk_blimp_red.png", + } +end + +steampunk_blimp.colors ={ + black='black', + blue='blue', + brown='brown', + cyan='cyan', + dark_green='dark_green', + dark_grey='dark_grey', + green='green', + grey='grey', + magenta='magenta', + orange='orange', + pink='pink', + red='red', + violet='violet', + white='white', + yellow='yellow', +} + +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "walk_map.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "utilities.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "control.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "fuel_management.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "engine_management.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "custom_physics.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "hud.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "entities.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "forms.lua") +dofile(minetest.get_modpath("steampunk_blimp") .. DIR_DELIM .. "manual.lua") + +-- +-- helpers and co. +-- + +function steampunk_blimp.get_hipotenuse_value(point1, point2) + return math.sqrt((point1.x - point2.x) ^ 2 + (point1.y - point2.y) ^ 2 + (point1.z - point2.z) ^ 2) +end + +function steampunk_blimp.dot(v1,v2) + return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z +end + +function steampunk_blimp.sign(n) + return n>=0 and 1 or -1 +end + +function steampunk_blimp.minmax(v,m) + return math.min(math.abs(v),m)*steampunk_blimp.sign(v) +end + +----------- +-- items +----------- +-- blimp +minetest.register_tool("steampunk_blimp:blimp", { + description = "Steampunk Blimp", + inventory_image = "steampunk_blimp_icon.png", + liquids_pointable = true, + stack_max = 1, + + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return + end + + local stack_meta = itemstack:get_meta() + local staticdata = stack_meta:get_string("staticdata") + + local pointed_pos = pointed_thing.under + --local node_below = minetest.get_node(pointed_pos).name + --local nodedef = minetest.registered_nodes[node_below] + + pointed_pos.y=pointed_pos.y+3 + local blimp = minetest.add_entity(pointed_pos, "steampunk_blimp:blimp", staticdata) + if blimp and placer then + local ent = blimp:get_luaentity() + ent._passengers = steampunk_blimp.copy_vector({[1]=nil, [2]=nil, [3]=nil, [4]=nil, [5]=nil, [6]=nil, [7]=nil}) + --minetest.chat_send_all('passengers: '.. dump(ent._passengers)) + local owner = placer:get_player_name() + ent.owner = owner + ent.hp = 50 --reset hp + blimp:set_yaw(placer:get_look_horizontal()) + itemstack:take_item() + airutils.create_inventory(ent, steampunk_blimp.trunk_slots, owner) + + local properties = ent.object:get_properties() + properties.infotext = owner .. " nice blimp" + blimp:set_properties(properties) + --steampunk_blimp.attach_pax(ent, placer) + end + + return itemstack + end, +}) + + +-- ephemeral blimp +minetest.register_craftitem("steampunk_blimp:ephemeral_blimp", { + description = "Ephemeral Blimp", + inventory_image = "steampunk_blimp_ephemeral_icon.png", + liquids_pointable = true, + + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return + end + + local pointed_pos = pointed_thing.under + --local node_below = minetest.get_node(pointed_pos).name + --local nodedef = minetest.registered_nodes[node_below] + + pointed_pos.y=pointed_pos.y+3 + local blimp = minetest.add_entity(pointed_pos, "steampunk_blimp:blimp") + if blimp and placer then + local ent = blimp:get_luaentity() + ent._passengers = steampunk_blimp.copy_vector({[1]=nil, [2]=nil, [3]=nil, [4]=nil, [5]=nil, [6]=nil, [7]=nil}) + --minetest.chat_send_all('passengers: '.. dump(ent._passengers)) + local owner = placer:get_player_name() + ent.owner = owner + ent._remove = true + ent._water_level = steampunk_blimp.MAX_WATER --start it full loaded + ent._energy = steampunk_blimp.MAX_FUEL --start it full loaded + ent._vehicle_name = "Ephemeral Blimp", + steampunk_blimp.paint(ent, "orange") + blimp:set_yaw(placer:get_look_horizontal()) + itemstack:take_item() + + local properties = ent.object:get_properties() + properties.infotext = owner .. " nice blimp" + blimp:set_properties(properties) + --steampunk_blimp.attach_pax(ent, placer) + end + + return itemstack + end, +}) + +if minetest.settings:get_bool('steampunk_blimp.enable_wind') then + steampunk_blimp.wind_enabled = true +else + steampunk_blimp.wind_enabled = false +end + +-- +-- crafting +-- + +if not minetest.settings:get_bool('steampunk_blimp.disable_craftitems') then + + local item_name = "steampunk_blimp:cylinder_part" + if airutils.is_repixture then + crafting.register_craft({ + output = item_name, + items = { + "group:fuzzy 8", + "rp_default:stick 4", + "group:planks 1", + } + }) + elseif airutils.is_mcl then + minetest.register_craft({ + output = item_name, + recipe = { + {"mcl_core:stick", "mcl_wool:white", "mcl_core:stick"}, + {"mcl_wool:white", "mcl_core:wood", "mcl_wool:white"}, + {"mcl_core:stick", "mcl_wool:white", "mcl_core:stick"}, + } + }) + else + minetest.register_craft({ + output = item_name, + recipe = { + {"default:stick", "wool:white", "default:stick"}, + {"wool:white", "group:wood", "wool:white"}, + {"default:stick", "wool:white", "default:stick"}, + } + }) + end + + item_name = "steampunk_blimp:cylinder" + if airutils.is_repixture then + crafting.register_craft({ + output = item_name, + items = { + "steampunk_blimp:cylinder_part 3", + } + }) + else + minetest.register_craft({ + output = item_name, + recipe = { + {"steampunk_blimp:cylinder_part", "steampunk_blimp:cylinder_part", "steampunk_blimp:cylinder_part"}, + } + }) + end + + item_name = "steampunk_blimp:rotor" + if airutils.is_repixture then + crafting.register_craft({ + output = item_name, + items = { + "group:fuzzy 3", + "rp_default:stick 3", + "rp_default:block_wrought_iron 1", + } + }) + elseif airutils.is_mcl then + minetest.register_craft({ + output = item_name, + recipe = { + {"mcl_wool:white", "mcl_core:stick", ""}, + {"mcl_wool:white", "mcl_core:stick", "mcl_core:ironblock"}, + {"mcl_wool:white", "mcl_core:stick", ""}, + } + }) + else + minetest.register_craft({ + output = item_name, + recipe = { + {"wool:white", "default:stick", ""}, + {"wool:white", "default:stick", "default:steelblock"}, + {"wool:white", "default:stick", ""}, + } + }) + end + + item_name = "steampunk_blimp:boiler" + if airutils.is_repixture then + crafting.register_craft({ + output = item_name, + items = { + "rp_default:ingot_wrought_iron 4", + "rp_default:block_wrought_iron 2", + } + }) + elseif airutils.is_mcl then + minetest.register_craft({ + output = item_name, + recipe = { + {"mcl_core:iron_ingot","mcl_core:iron_ingot"}, + {"mcl_core:ironblock","mcl_core:iron_ingot",}, + {"mcl_core:ironblock","mcl_core:iron_ingot"}, + } + }) + else + minetest.register_craft({ + output = item_name, + recipe = { + {"default:steel_ingot","default:steel_ingot"}, + {"default:steelblock","default:steel_ingot",}, + {"default:steelblock","default:steel_ingot"}, + } + }) + end + + item_name = "steampunk_blimp:boat" + if airutils.is_repixture then + crafting.register_craft({ + output = item_name, + items = { + "group:planks 6", + "steampunk_blimp:rotor 2", + "steampunk_blimp:boiler 1", + } + }) + else + minetest.register_craft({ + output = item_name, + recipe = { + {"group:wood", "group:wood", "steampunk_blimp:rotor"}, + {"group:wood", "steampunk_blimp:boiler", "group:wood"}, + {"group:wood", "group:wood", "steampunk_blimp:rotor"}, + } + }) + end + + item_name = "steampunk_blimp:blimp" + if airutils.is_repixture then + crafting.register_craft({ + output = item_name, + items = { + "steampunk_blimp:cylinder 1", + "steampunk_blimp:boat 1", + } + }) + else + minetest.register_craft({ + output = item_name, + recipe = { + {"steampunk_blimp:cylinder",}, + {"steampunk_blimp:boat",}, + } + }) + end + + + -- cylinder section + minetest.register_craftitem("steampunk_blimp:cylinder_part",{ + description = "steampunk_blimp cylinder section", + inventory_image = "steampunk_blimp_cylinder_part.png", + }) + + -- cylinder + minetest.register_craftitem("steampunk_blimp:cylinder",{ + description = "steampunk_blimp cylinder", + inventory_image = "steampunk_blimp_cylinder.png", + }) + + -- boiler + minetest.register_craftitem("steampunk_blimp:boiler",{ + description = "steampunk_blimp boiler", + inventory_image = "steampunk_blimp_boiler.png", + }) + + -- boiler + minetest.register_craftitem("steampunk_blimp:rotor",{ + description = "steampunk_blimp rotor", + inventory_image = "steampunk_blimp_rotor.png", + }) + + -- fuselage + minetest.register_craftitem("steampunk_blimp:boat",{ + description = "steampunk_blimp fuselage", + inventory_image = "steampunk_blimp_boat.png", + }) +end + diff --git a/mods/steampunk_blimp/manual.lua b/mods/steampunk_blimp/manual.lua new file mode 100644 index 00000000..b5b42fa4 --- /dev/null +++ b/mods/steampunk_blimp/manual.lua @@ -0,0 +1,91 @@ +-------------- +-- Manual -- +-------------- + +function steampunk_blimp.manual_formspec(name) + local basic_form = table.concat({ + "formspec_version[3]", + "size[6,6]" + }, "") + + basic_form = basic_form.."button[1,1.0;4,1;short;Shortcuts]" + basic_form = basic_form.."button[1,2.5;4,1;fuel;Refueling]" + basic_form = basic_form.."button[1,4.0;4,1;share;Sharing]" + + minetest.show_formspec(name, "steampunk_blimp:manual_main", basic_form) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "steampunk_blimp:manual_main" then + local formspec_color = "#44444466" + if fields.short then + local text = { + "Shortcuts \n\n", + "* Right click: enter in / acess the internal menu \n", + "* Punch with dye to paint the primary color\n", + "* Punch a dye, but holding Aux (E) key to change the secondary color.\n", + "* To change the blimp logo, call the command \""..core.colorize('#ffff00', "/blimp_logo").."\".\n", + "* Forward or backward while in drive position: controls the power lever \n", + "* Left or right while in drive position: controls the direction \n", + "* Jump and sneak: controls the up and down movement \n", + "* Aux (E) + right click while inside: acess inventory \n", + "* Aux (E) + backward while in drive position: the machine does backward \n", + "* Aux (E) + foward while in drive position: extra power \n" + } + local shortcut_form = table.concat({ + "formspec_version[3]", + "size[16,10]", + "no_prepend[]", + "bgcolor["..formspec_color..";false]", + "label[1.0,2.0;", table.concat(text, ""), "]", + }, "") + minetest.show_formspec(player:get_player_name(), "steampunk_blimp:manual_shortcut", shortcut_form) + end + if fields.fuel then + local text = { + "Fuel \n\n", + "To fly it, it is necessary to provide some items, such as fuel to be burned and \n", + "water for the boiler. The fuel can be coal, coal block or wood. To supply it, \n", + "be on board and punch the necessary items on the airship.\n", + "There is another way to load water to the boiler: if it is landed on water, it can load \n", + "it through the menu. But the current pressure will be lost. \n" + } + local fuel_form = table.concat({ + "formspec_version[3]", + "size[16,10]", + "no_prepend[]", + "bgcolor["..formspec_color..";false]", + "label[1.0,2.0;", table.concat(text, ""), "]", + }, "") + minetest.show_formspec(player:get_player_name(), "steampunk_blimp:fuel", fuel_form) + end + if fields.share then + local text = { + "Sharing \n\n", + "This vehicle was made to be shared with a team. So the owner can set more users to \n", + "operate it. Inside the blimp, just use the command \""..core.colorize('#ffff00', "/blimp_share ").."\" \n", + "To remove someone from the sharing, \""..core.colorize('#ffff00', "/blimp_remove ").."\" \n", + "To list the owners, \""..core.colorize('#ffff00', "/blimp_list").."\" \n", + "Is possible to lock the blimp access, so only the owners can enter: \""..core.colorize('#ffff00', "/blimp_lock true").."\" \n", + "To let anyone enter, \""..core.colorize('#ffff00', "/blimp_lock false").."\" \n", + "All shared owners can access the blimp inventory" + } + local tips_form = table.concat({ + "formspec_version[3]", + "size[16,10]", + "no_prepend[]", + "bgcolor["..formspec_color..";false]", + "label[1,2;", table.concat(text, ""), "]", + }, "") + minetest.show_formspec(player:get_player_name(), "steampunk_blimp:share", tips_form) + end + end +end) + +minetest.register_chatcommand("blimp_manual", { + params = "", + description = "Blimp manual", + func = function(name, param) + steampunk_blimp.manual_formspec(name) + end +}) diff --git a/mods/steampunk_blimp/mod.conf b/mods/steampunk_blimp/mod.conf new file mode 100644 index 00000000..caac5773 --- /dev/null +++ b/mods/steampunk_blimp/mod.conf @@ -0,0 +1,7 @@ +name = steampunk_blimp +depends = airutils +optional_depends = rp_crafting +author = apercy +description = Adds a steampunk blimp +title = Steampunk Blimp +release = 30012 diff --git a/mods/steampunk_blimp/models/steampunk_blimp.b3d b/mods/steampunk_blimp/models/steampunk_blimp.b3d new file mode 100644 index 00000000..f0ffa3ac Binary files /dev/null and b/mods/steampunk_blimp/models/steampunk_blimp.b3d differ diff --git a/mods/steampunk_blimp/models/steampunk_blimp_light.b3d b/mods/steampunk_blimp/models/steampunk_blimp_light.b3d new file mode 100644 index 00000000..23021488 Binary files /dev/null and b/mods/steampunk_blimp/models/steampunk_blimp_light.b3d differ diff --git a/mods/steampunk_blimp/models/steampunk_blimp_stand_base.b3d b/mods/steampunk_blimp/models/steampunk_blimp_stand_base.b3d new file mode 100644 index 00000000..ccf1db3b Binary files /dev/null and b/mods/steampunk_blimp/models/steampunk_blimp_stand_base.b3d differ diff --git a/mods/steampunk_blimp/optimize_textures.sh b/mods/steampunk_blimp/optimize_textures.sh new file mode 100644 index 00000000..e15dde97 --- /dev/null +++ b/mods/steampunk_blimp/optimize_textures.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Colors with 0 alpha need to be preserved, because opaque leaves ignore alpha. +# For that purpose, the use of indexed colors is disabled (-nc). + +find -name 'textures/*.png' -print0 | xargs -0 optipng -o7 -zm1-9 -nc -strip all -clobber diff --git a/mods/steampunk_blimp/screenshot.jpg b/mods/steampunk_blimp/screenshot.jpg new file mode 100644 index 00000000..f1fc30e1 Binary files /dev/null and b/mods/steampunk_blimp/screenshot.jpg differ diff --git a/mods/steampunk_blimp/sounds/steampunk_blimp_collision.ogg b/mods/steampunk_blimp/sounds/steampunk_blimp_collision.ogg new file mode 100644 index 00000000..b15fbb1e Binary files /dev/null and b/mods/steampunk_blimp/sounds/steampunk_blimp_collision.ogg differ diff --git a/mods/steampunk_blimp/sounds/steampunk_blimp_rope.ogg b/mods/steampunk_blimp/sounds/steampunk_blimp_rope.ogg new file mode 100644 index 00000000..4572bc30 Binary files /dev/null and b/mods/steampunk_blimp/sounds/steampunk_blimp_rope.ogg differ diff --git a/mods/steampunk_blimp/textures/blimp_clover.png b/mods/steampunk_blimp/textures/blimp_clover.png new file mode 100644 index 00000000..b8076825 Binary files /dev/null and b/mods/steampunk_blimp/textures/blimp_clover.png differ diff --git a/mods/steampunk_blimp/textures/blimp_jack.png b/mods/steampunk_blimp/textures/blimp_jack.png new file mode 100644 index 00000000..fc64fa08 Binary files /dev/null and b/mods/steampunk_blimp/textures/blimp_jack.png differ diff --git a/mods/steampunk_blimp/textures/blimp_liz.png b/mods/steampunk_blimp/textures/blimp_liz.png new file mode 100644 index 00000000..47fdc31d Binary files /dev/null and b/mods/steampunk_blimp/textures/blimp_liz.png differ diff --git a/mods/steampunk_blimp/textures/blimp_shotting_star.png b/mods/steampunk_blimp/textures/blimp_shotting_star.png new file mode 100644 index 00000000..77caccf7 Binary files /dev/null and b/mods/steampunk_blimp/textures/blimp_shotting_star.png differ diff --git a/mods/steampunk_blimp/textures/blimp_skull.png b/mods/steampunk_blimp/textures/blimp_skull.png new file mode 100644 index 00000000..1bc8e820 Binary files /dev/null and b/mods/steampunk_blimp/textures/blimp_skull.png differ diff --git a/mods/steampunk_blimp/textures/blimp_xmas.png b/mods/steampunk_blimp/textures/blimp_xmas.png new file mode 100644 index 00000000..f8b69593 Binary files /dev/null and b/mods/steampunk_blimp/textures/blimp_xmas.png differ diff --git a/mods/steampunk_blimp/textures/numeros.xcf b/mods/steampunk_blimp/textures/numeros.xcf new file mode 100644 index 00000000..750d96e8 Binary files /dev/null and b/mods/steampunk_blimp/textures/numeros.xcf differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_0.png b/mods/steampunk_blimp/textures/steampunk_blimp_0.png new file mode 100644 index 00000000..88c48293 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_0.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_1.png b/mods/steampunk_blimp/textures/steampunk_blimp_1.png new file mode 100644 index 00000000..3f2d714e Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_1.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_2.png b/mods/steampunk_blimp/textures/steampunk_blimp_2.png new file mode 100644 index 00000000..125cfb95 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_2.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_3.png b/mods/steampunk_blimp/textures/steampunk_blimp_3.png new file mode 100644 index 00000000..94295fe5 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_3.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_4.png b/mods/steampunk_blimp/textures/steampunk_blimp_4.png new file mode 100644 index 00000000..38e28333 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_4.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_5.png b/mods/steampunk_blimp/textures/steampunk_blimp_5.png new file mode 100644 index 00000000..680608de Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_5.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_6.png b/mods/steampunk_blimp/textures/steampunk_blimp_6.png new file mode 100644 index 00000000..95cb29f3 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_6.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_7.png b/mods/steampunk_blimp/textures/steampunk_blimp_7.png new file mode 100644 index 00000000..4ac2dffc Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_7.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_8.png b/mods/steampunk_blimp/textures/steampunk_blimp_8.png new file mode 100644 index 00000000..b1f467ec Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_8.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_9.png b/mods/steampunk_blimp/textures/steampunk_blimp_9.png new file mode 100644 index 00000000..90034ab6 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_9.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_alpha.png b/mods/steampunk_blimp/textures/steampunk_blimp_alpha.png new file mode 100644 index 00000000..1397ecc4 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_alpha.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_alpha_logo.png b/mods/steampunk_blimp/textures/steampunk_blimp_alpha_logo.png new file mode 100644 index 00000000..1397ecc4 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_alpha_logo.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_boat.png b/mods/steampunk_blimp/textures/steampunk_blimp_boat.png new file mode 100644 index 00000000..82c1b219 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_boat.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_boiler.png b/mods/steampunk_blimp/textures/steampunk_blimp_boiler.png new file mode 100644 index 00000000..180ad34b Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_boiler.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_compass.png b/mods/steampunk_blimp/textures/steampunk_blimp_compass.png new file mode 100644 index 00000000..e3154d16 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_compass.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_cylinder.png b/mods/steampunk_blimp/textures/steampunk_blimp_cylinder.png new file mode 100644 index 00000000..052599b8 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_cylinder.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_cylinder_part.png b/mods/steampunk_blimp/textures/steampunk_blimp_cylinder_part.png new file mode 100644 index 00000000..04129066 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_cylinder_part.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_ephemeral_icon.png b/mods/steampunk_blimp/textures/steampunk_blimp_ephemeral_icon.png new file mode 100644 index 00000000..9e2fe76d Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_ephemeral_icon.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_forno_mask.png b/mods/steampunk_blimp/textures/steampunk_blimp_forno_mask.png new file mode 100644 index 00000000..45c552b7 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_forno_mask.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_hud_panel.png b/mods/steampunk_blimp/textures/steampunk_blimp_hud_panel.png new file mode 100644 index 00000000..7e9915db Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_hud_panel.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_hud_panel.xcf b/mods/steampunk_blimp/textures/steampunk_blimp_hud_panel.xcf new file mode 100644 index 00000000..be433c5c Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_hud_panel.xcf differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_icon.png b/mods/steampunk_blimp/textures/steampunk_blimp_icon.png new file mode 100644 index 00000000..e5d4d155 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_icon.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_ind_box.png b/mods/steampunk_blimp/textures/steampunk_blimp_ind_box.png new file mode 100644 index 00000000..52a50b75 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_ind_box.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_rope.png b/mods/steampunk_blimp/textures/steampunk_blimp_rope.png new file mode 100644 index 00000000..60afeebc Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_rope.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_rotor.png b/mods/steampunk_blimp/textures/steampunk_blimp_rotor.png new file mode 100644 index 00000000..5fc5981b Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_rotor.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_rotor_mask.png b/mods/steampunk_blimp/textures/steampunk_blimp_rotor_mask.png new file mode 100644 index 00000000..1e5eda64 Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_rotor_mask.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_rotor_mask2.png b/mods/steampunk_blimp/textures/steampunk_blimp_rotor_mask2.png new file mode 100644 index 00000000..4c38ceed Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_rotor_mask2.png differ diff --git a/mods/steampunk_blimp/textures/steampunk_blimp_throttle.png b/mods/steampunk_blimp/textures/steampunk_blimp_throttle.png new file mode 100644 index 00000000..599660dd Binary files /dev/null and b/mods/steampunk_blimp/textures/steampunk_blimp_throttle.png differ diff --git a/mods/steampunk_blimp/utilities.lua b/mods/steampunk_blimp/utilities.lua new file mode 100644 index 00000000..5423ac4c --- /dev/null +++ b/mods/steampunk_blimp/utilities.lua @@ -0,0 +1,505 @@ +function steampunk_blimp.testDamage(self, velocity, position) + if self._last_accell == nil then return end + local p = position --self.object:get_pos() + local collision = false + local low_node_pos = -2.5 + if self._last_vel == nil then return end + --lets calculate the vertical speed, to avoid the bug on colliding on floor with hard lag + if math.abs(velocity.y - self._last_vel.y) > 2 then + local noded = airutils.nodeatpos(airutils.pos_shift(p,{y=low_node_pos})) + if (noded and noded.drawtype ~= 'airlike') then + collision = true + else + self.object:set_velocity(self._last_vel) + --self.object:set_acceleration(self._last_accell) + self.object:set_velocity(vector.add(velocity, vector.multiply(self._last_accell, self.dtime/8))) + end + end + local impact = math.abs(steampunk_blimp.get_hipotenuse_value(velocity, self._last_vel)) + if impact > 2 then + if self.colinfo then + collision = self.colinfo.collides + --core.chat_send_all(impact) + end + end + + if collision then + --self.object:set_velocity({x=0,y=0,z=0}) + local damage = impact -- / 2 + core.sound_play("steampunk_blimp_collision", { + --to_player = self.driver_name, + object = self.object, + max_hear_distance = 15, + gain = 1.0, + fade = 0.0, + pitch = 1.0, + }, true) + if damage > 5 then + self._power_lever = 0 + end + + if self.driver_name then + local player_name = self.driver_name + + local player = core.get_player_by_name(player_name) + if player then + if player:get_hp() > 0 then + player:set_hp(player:get_hp()-(damage/2)) + end + end + if self._passenger ~= nil then + local passenger = core.get_player_by_name(self._passenger) + if passenger then + if passenger:get_hp() > 0 then + passenger:set_hp(passenger:get_hp()-(damage/2)) + end + end + end + end + + end +end + +local function do_attach(self, player, slot) + if slot == 0 then return end + if self._passengers[slot] == nil then + local name = player:get_player_name() + --core.chat_send_all(self.driver_name) + self._passengers[slot] = name + player:set_attach(self._passengers_base[slot], "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) + + if airutils.is_mcl then + mcl_player.player_attached[name] = true + elseif airutils.is_repixture then + rp_player.player_attached[name] = true + else + player_api.player_attached[name] = true + end + end +end + +function steampunk_blimp.check_passenger_is_attached(self, name) + local is_attached = false + if is_attached == false then + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers[i] == name then + is_attached = true + break + end + end + end + return is_attached +end + +--this method checks each 1 second for a disconected player who comes back +function steampunk_blimp.rescueConnectionFailedPassengers(self) + self._disconnection_check_time = self._disconnection_check_time + self.dtime + if self._disconnection_check_time > 1 then + --core.chat_send_all(dump(self._passengers)) + self._disconnection_check_time = 0 + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers[i] then + local player = core.get_player_by_name(self._passengers[i]) + if player then --we have a player! + local is_attached = nil + if airutils.is_mcl then + is_attached = mcl_player.player_attached[self._passengers[i]] + elseif airutils.is_repixture then + is_attached = rp_player.player_attached[self._passengers[i]] + else + is_attached = player_api.player_attached[self._passengers[i]] + end + + if is_attached == nil then --but isn't attached? + --core.chat_send_all("okay") + if player:get_hp() > 0 then + self._passengers[i] = nil --clear the slot first + do_attach(self, player, i) --attach + end + end + end + end + end + end +end + +-- attach passenger +function steampunk_blimp.attach_pax(self, player, slot) + slot = slot or 0 + + --verify if is locked to non-owners + if self._passengers_locked == true then + local name = player:get_player_name() + local can_bypass = core.check_player_privs(player, {protection_bypass=true}) + local is_shared = false + if name == self.owner or can_bypass then is_shared = true end + for k, v in pairs(self._shared_owners) do + if v == name then + is_shared = true + break + end + end + if is_shared == false then + core.chat_send_player(name,core.colorize('#ff0000', " >>> This blimp is currently locked for non-owners")) + return + end + end + + + if slot > 0 then + do_attach(self, player, slot) + return + end + --core.chat_send_all(dump(self._passengers)) + + --now yes, lets attach the player + --randomize the seat + local t = {1,2,3,4,5,6,7} + for i = 1, #t*2 do + local a = math.random(#t) + local b = math.random(#t) + t[a],t[b] = t[b],t[a] + end + + --core.chat_send_all(dump(t)) + + for k,v in ipairs(t) do + local i = t[k] or 0 + if self._passengers[i] == nil then + do_attach(self, player, i) + --core.chat_send_all(i) + break + end + end +end + +function steampunk_blimp.dettach_pax(self, player, side) + side = side or "r" + if player then + local name = player:get_player_name() --self._passenger + if self.driver_name == name then + self.driver_name = nil + self._at_control = false + end + + steampunk_blimp.remove_hud(player) + + -- passenger clicked the object => driver gets off the vehicle + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers[i] == name then + self._passengers[i] = nil + self._passengers_base_pos[i] = steampunk_blimp.copy_vector(steampunk_blimp.passenger_pos[i]) + --break + end + end + + -- detach the player + player:set_detach() + if airutils.is_mcl then + mcl_player.player_attached[name] = nil + mcl_player.player_set_animation(player, "stand", 30) + elseif airutils.is_repixture then + rp_player.player_attached[name] = nil + rp_player.player_set_animation(player, "stand", 30) + else + player_api.player_attached[name] = nil + player_api.set_animation(player, "stand") + end + + -- move player down + core.after(0.1, function(pos) + local rotation = self.object:get_rotation() + local direction = rotation.y + + if side == "l" then + direction = direction - math.rad(180) + end + + local move = 5 + pos.x = pos.x + move * math.cos(direction) + pos.z = pos.z + move * math.sin(direction) + if self.isinliquid then + pos.y = pos.y + 1 + else + pos.y = pos.y - 2.5 + end + player:set_pos(pos) + end, player:get_pos()) + end +end + +function steampunk_blimp.textures_copy() + local tablecopy = {} + for k, v in pairs(steampunk_blimp.textures) do + tablecopy[k] = v + end + return tablecopy +end + +--this function needs an urgent refactory to be independent, but not today :( +local function paint(self, write_prefix) + write_prefix = write_prefix or false + + local l_textures = steampunk_blimp.textures_copy() + for _, texture in ipairs(l_textures) do + local indx = texture:find(steampunk_blimp.color1_texture) + if indx then + if not airutils.is_repixture then + l_textures[_] = "wool_".. self.color..".png" + else + l_textures[_] = "rp_default_reed_block_side.png^[colorize:"..airutils.colors[self.color] + end + end + indx = texture:find(steampunk_blimp.color2_texture) + if indx then + if not airutils.is_repixture then + l_textures[_] = "wool_".. self.color2..".png" + else + l_textures[_] = "rp_default_reed_block_side.png^[colorize:"..airutils.colors[self.color2] + end + end + + indx = texture:find('steampunk_blimp_alpha_logo.png') + if indx then + l_textures[_] = self.logo + end + if airutils._use_signs_api and write_prefix == true then + indx = texture:find('airutils_name_canvas.png') + if indx then + l_textures[_] = "airutils_name_canvas.png^"..airutils.convert_text_to_texture(self._ship_name, self._name_color or 0, self._name_hor_aligment or 0.8) + end + end + end + self.object:set_properties({textures=l_textures}) +end + +function steampunk_blimp.set_logo(self, texture_name) + if texture_name == "" or texture_name == nil then + self.logo = "steampunk_blimp_alpha_logo.png" + elseif texture_name then + self.logo = texture_name + end + paint(self) +end + +--painting +function steampunk_blimp.paint(self, colstr) + if colstr then + self.color = colstr + paint(self) + end +end +function steampunk_blimp.paint2(self, colstr) + if colstr then + self.color2 = colstr + paint(self,true) + end +end + +-- destroy the boat +function steampunk_blimp.destroy(self, overload) + if self.sound_handle then + core.sound_stop(self.sound_handle) + self.sound_handle = nil + end + + local pos = self.object:get_pos() + if self.fire then self.fire:remove() end + + for i = steampunk_blimp.max_seats,1,-1 + do + if self._passengers_base[i] then self._passengers_base[i]:remove() end + end + + airutils.destroy_inventory(self) + self.inv = nil + self._inv_id = nil + + local remove_it = self._remove or false + + local lua_ent = self.object:get_luaentity() + local staticdata = lua_ent:get_staticdata(self) + local player = core.get_player_by_name(self.owner) + + self.object:remove() + + if remove_it == false then + pos.y=pos.y+2 + --[[for i=1,7 do + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:steel_ingot') + end + + for i=1,7 do + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:mese_crystal') + end]]-- + + --core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'steampunk_blimp:boat') + --core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5},'default:diamond') + + local stack = ItemStack(self.item) + local stack_meta = stack:get_meta() + stack_meta:set_string("staticdata", staticdata) + + if player then + local inv = player:get_inventory() + if inv then + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + else + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) + end + end + else + core.add_item({x=pos.x+math.random()-0.5,y=pos.y,z=pos.z+math.random()-0.5}, stack) + end + end +end + +--returns 0 for old, 1 for new +function steampunk_blimp.detect_player_api(player) + local player_proterties = player:get_properties() + local mesh = "character.b3d" + if player_proterties.mesh == mesh then + local models = player_api.registered_models + local character = models[mesh] + if character then + if character.animations.sit.eye_height then + return 1 + else + return 0 + end + end + end + + return 0 +end + +function steampunk_blimp.checkAttach(self, player) + local retVal = false + if player then + local player_attach = player:get_attach() + if player_attach then + for i = steampunk_blimp.max_seats,1,-1 + do + if player_attach == self._passengers_base[i] then + retVal = true + break + end + end + end + end + return retVal +end + +function steampunk_blimp.clamp(value, min, max) + local retVal = value + if value < min then retVal = min end + if value > max then retVal = max end + --core.chat_send_all(value .. " - " ..retVal) + return retVal +end + +function steampunk_blimp.reclamp(value, min, max) + local retVal = value + local mid = (max-min)/2 + if value > min and value <= (min+mid) then retVal = min end + if value < max and value > (max-mid) then retVal = max end + --core.chat_send_all(value .. " - return: " ..retVal .. " - mid: " .. mid) + return retVal +end + +function steampunk_blimp.engineSoundPlay(self) + --sound + if self.sound_handle then core.sound_stop(self.sound_handle) end + if self.sound_handle_pistons then core.sound_stop(self.sound_handle_pistons) end + if self.object then + local furnace_sound = "default_furnace_active" + if steampunk_blimp.furnace_sound then + self.sound_handle = core.sound_play({name = steampunk_blimp.furnace_sound.name}, + {object = self.object, gain = steampunk_blimp.furnace_sound.gain, + max_hear_distance = 5, + loop = true,}) + end + + self.sound_handle_pistons = core.sound_play({name = steampunk_blimp.piston_sound.name},--"default_item_smoke"}, + {object = self.object, gain = steampunk_blimp.piston_sound.gain, + pitch = steampunk_blimp.piston_sound.pitch+((math.abs(self._power_lever)/100)/2), + max_hear_distance = 32, + loop = true,}) + end +end + +function steampunk_blimp.engine_set_sound_and_animation(self) + if self._last_applied_power ~= self._power_lever then + --core.chat_send_all('test2') + self._last_applied_power = self._power_lever + self.object:set_animation_frame_speed(steampunk_blimp.iddle_rotation + (self._power_lever)) + if self._last_sound_update == nil then self._last_sound_update = self._power_lever end + if math.abs(self._last_sound_update - self._power_lever) > 5 then + self._last_sound_update = self._power_lever + steampunk_blimp.engineSoundPlay(self) + end + end + if self._engine_running == false then + if self.sound_handle then + core.sound_stop(self.sound_handle) + self.sound_handle = nil + --self.object:set_animation_frame_speed(0) + end + end +end + + +function steampunk_blimp.start_furnace(self) + if self._engine_running then + self._engine_running = false + -- sound and animation + if self.sound_handle then + core.sound_stop(self.sound_handle) + self.sound_handle = nil + end + elseif self._engine_running == false and self._energy > 0 then + self._engine_running = true + -- sound + if self.sound_handle then core.sound_stop(self.sound_handle) end + if self.object then + local furnace_sound = "default_furnace_active" + if airutils.is_mcl then furnace_sound = "fire_fire" end + + if steampunk_blimp.furnace_sound then + self.sound_handle = core.sound_play({name = steampunk_blimp.furnace_sound.name}, + {object = self.object, gain = steampunk_blimp.furnace_sound.gain, + max_hear_distance = 5, + loop = true,}) + end + end + end +end + +function steampunk_blimp.copy_vector(original_vector) + local tablecopy = {} + for k, v in pairs(original_vector) do + tablecopy[k] = v + end + return tablecopy +end + +function steampunk_blimp.play_rope_sound(self) + core.sound_play({name = "steampunk_blimp_rope"}, + {object = self.object, gain = 1, + max_hear_distance = 5, + ephemeral = true,}) +end + +function steampunk_blimp.table_copy(table_here) + local tablecopy = {} + for k, v in pairs(table_here) do + tablecopy[k] = v + end + return tablecopy +end + + diff --git a/mods/steampunk_blimp/walk_map.lua b/mods/steampunk_blimp/walk_map.lua new file mode 100644 index 00000000..9bd270ba --- /dev/null +++ b/mods/steampunk_blimp/walk_map.lua @@ -0,0 +1,312 @@ +function steampunk_blimp.clamp(value, min, max) + local retVal = value + if value < min then retVal = min end + if value > max then retVal = max end + --minetest.chat_send_all(value .. " - " ..retVal) + return retVal +end + +function steampunk_blimp.reclamp(value, min, max) + local retVal = value + local mid = (max-min)/2 + if value > min and value <= (min+mid) then retVal = min end + if value < max and value > (max-mid) then retVal = max end + --minetest.chat_send_all(value .. " - return: " ..retVal .. " - mid: " .. mid) + return retVal +end + +local function is_obstacle_zone(pos, start_point, end_point) + local retVal = steampunk_blimp.table_copy(pos) + + local min_x, min_z, max_x, max_z + + if start_point.x <= end_point.x then min_x = start_point.x else min_x = end_point.x end + if start_point.z <= end_point.z then min_z = start_point.z else min_z = end_point.z end + if start_point.x > end_point.x then max_x = start_point.x else max_x = end_point.x end + if start_point.z > end_point.z then max_z = start_point.z else max_z = end_point.z end + + local mid_x = (max_x - min_x)/2 + local mid_z = (max_z - min_z)/2 + + if pos.x < max_x and pos.x > min_x+mid_x and + pos.z < max_z and pos.z > min_z then + retVal.x = max_x + 1 + return retVal + end + if pos.x > min_x and pos.x <= min_x+mid_x and + pos.z < max_z and pos.z > min_z then + retVal.x = min_x - 1 + return retVal + end + + local death_zone = 1.5 --to avoid the "slip" when colliding in y direction + if pos.z < max_z + death_zone and pos.z > min_z+mid_z and + pos.x > min_x and pos.x < max_x then + retVal.z = max_z + 1 + return retVal + end + if pos.z > min_z - death_zone and pos.z <= min_z+mid_z and + pos.x > min_x and pos.x < max_x then + retVal.z = min_z - 1 + return retVal + end + + return retVal +end + + +function steampunk_blimp.boat_upper_deck_map(pos, dpos) + local orig_pos = steampunk_blimp.copy_vector(pos) + local position = steampunk_blimp.copy_vector(dpos) + local new_pos = steampunk_blimp.copy_vector(dpos) + + new_pos.z = steampunk_blimp.clamp(new_pos.z, -47, -16) + new_pos = is_obstacle_zone(new_pos, {x=4, z=-28}, {x=-4, z=-20}) --timao + new_pos = is_obstacle_zone(new_pos, {x=-30, z=-24}, {x=4, z=-12}) + + if position.z >= -49 and position.z < -32 then --limit 10 + new_pos.y = 20.821 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -8, 8) + return new_pos + end + if position.z >= -32 and position.z < -14 then --limit 11 + new_pos.y = 20.821 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -11, 11) + + if position.z > -24 then --escada + if orig_pos.x <= 4 then + new_pos.z = steampunk_blimp.reclamp(new_pos.z, -24, -12) + end + end + return new_pos + end + return new_pos +end + +local function is_ladder_zone(pos) + local ladder_zone = false + if pos.z <= -12 and pos.z >= -18 and pos.x > 4 and pos.x < 12 then ladder_zone = true end + return ladder_zone +end + +function steampunk_blimp.boat_lower_deck_map(pos, dpos) + local position = steampunk_blimp.copy_vector(dpos) + local new_pos = steampunk_blimp.copy_vector(dpos) + new_pos.z = steampunk_blimp.clamp(new_pos.z, -29, 45) + + if position.z > -31 and position.z < -14 then --limit 10 + new_pos.y = 0 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -10, 10) + new_pos = is_obstacle_zone(new_pos, {x=-6, z=-9}, {x=6, z=14}) --caldeira + return new_pos + end + + if position.z >= -14 and position.z < -4 then --limit 11 + new_pos.y = 0 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -12, 12) + new_pos = is_obstacle_zone(new_pos, {x=-6, z=-9}, {x=6, z=14}) --caldeira + return new_pos + end + + if position.z >= -4 and position.z <= 4 then --limit 14 + new_pos.y = 0 + new_pos.x = steampunk_blimp.clamp(position.x, -14, 14) + new_pos = is_obstacle_zone(new_pos, {x=-6, z=-9}, {x=6, z=14}) --caldeira + return new_pos + end + + if position.z > 4 and position.z <= 19 then --limit 11 + new_pos.y = 0 + new_pos.x = steampunk_blimp.clamp(position.x, -12, 12) + new_pos = is_obstacle_zone(new_pos, {x=-6, z=-9}, {x=6, z=14}) --caldeira + return new_pos + end + + if position.z > 19 and position.z <= 22 then --limit 10 + new_pos.y = 4.4 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -10, 10) + return new_pos + end + + if position.z > 22 and position.z <= 30 then --limit 7 + new_pos.y = 8.5 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -7, 7) + return new_pos + end + + if position.z > 30 and position.z <= 36 then --limit 5 + new_pos.y = 8.5 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -5, 5) + return new_pos + end + + if position.z > 36 and position.z < 47 then --limit 1 + new_pos.y = 8.5 + new_pos.x = steampunk_blimp.clamp(new_pos.x, -2, 2) + return new_pos + end + + return new_pos +end + +function steampunk_blimp.ladder_map(pos, dpos) + local position = steampunk_blimp.copy_vector(dpos) + local new_pos = steampunk_blimp.copy_vector(dpos) + new_pos.z = steampunk_blimp.clamp(new_pos.z, -18, -12) + if position.z > -20 and position.z < -10 then --limit 10 + new_pos.x = steampunk_blimp.clamp(new_pos.x, 4, 12) + end + return new_pos +end + +function steampunk_blimp.navigate_deck(pos, dpos, player) + local pos_d = dpos + local ladder_zone = is_ladder_zone(pos) + + local upper_deck_y = 20.821 + local lower_deck_y = 0 + if player then + if pos.y == upper_deck_y then + pos_d = steampunk_blimp.boat_upper_deck_map(pos, dpos) + elseif pos.y <= 8.5 and pos.y >= 0 then + if ladder_zone == false then + pos_d = steampunk_blimp.boat_lower_deck_map(pos, dpos) + end + elseif pos.y > 8.5 and pos.y < upper_deck_y then + pos_d = steampunk_blimp.ladder_map(pos, dpos) + end + + local ctrl = player:get_player_control() + if ctrl.jump or ctrl.sneak then --ladder + if ladder_zone then + --minetest.chat_send_all(dump(pos)) + if ctrl.jump then + pos_d.y = pos_d.y + 0.9 + if pos_d.y > upper_deck_y then pos_d.y = upper_deck_y end + end + if ctrl.sneak then + pos_d.y = pos_d.y - 0.9 + if pos_d.y < lower_deck_y then pos_d.y = lower_deck_y end + end + end + end + end + --minetest.chat_send_all(dump(pos_d)) + + return pos_d +end + +--note: index variable just for the walk +--this function was improved by Auri Collings on steampumove_personsnk_blimp +local function get_result_pos(self, player, index) + local pos = nil + if player then + local ctrl = player:get_player_control() + + local direction = player:get_look_horizontal() + local rotation = self.object:get_rotation() + direction = direction - rotation.y + + pos = vector.new() + + local y_rot = -math.deg(direction) + pos.y = y_rot --okay, this is strange to keep here, but as I dont use it anyway... + + + if ctrl.up or ctrl.down or ctrl.left or ctrl.right then + if airutils.is_mcl then + mcl_player.player_set_animation(player, "walk", 30) + elseif airutils.is_repixture then + rp_player.player_set_animation(player, "walk", 30) + else + player_api.set_animation(player, "walk", 30) + end + + local speed = 0.4 + + local dir = vector.new(ctrl.up and -1 or ctrl.down and 1 or 0, 0, ctrl.left and 1 or ctrl.right and -1 or 0) + dir = vector.normalize(dir) + dir = vector.rotate(dir, {x = 0, y = -direction, z = 0}) + + local time_correction = (self.dtime/steampunk_blimp.ideal_step) + local move = speed * time_correction + + pos.x = move * dir.x + pos.z = move * dir.z + + --lets fake walk sound + if self._passengers_base_pos[index].dist_moved == nil then self._passengers_base_pos[index].dist_moved = 0 end + self._passengers_base_pos[index].dist_moved = self._passengers_base_pos[index].dist_moved + move; + if math.abs(self._passengers_base_pos[index].dist_moved) > 5 then + self._passengers_base_pos[index].dist_moved = 0 + minetest.sound_play({name = steampunk_blimp.steps_sound.name}, + {object = player, gain = steampunk_blimp.steps_sound.gain, + max_hear_distance = 5, + ephemeral = true,}) + end + else + if airutils.is_mcl then + mcl_player.player_set_animation(player, "stand", 30) + elseif airutils.is_repixture then + rp_player.player_set_animation(player, "stand", 30) + else + player_api.set_animation(player, "stand", 30) + end + end + end + return pos +end + + +function steampunk_blimp.move_persons(self) + --self._passenger = nil + if self.object == nil then return end + + for i = steampunk_blimp.max_seats,1,-1 + do + local player = nil + if self._passengers[i] then player = minetest.get_player_by_name(self._passengers[i]) end + + if self.driver_name and self._passengers[i] == self.driver_name then + --clean driver if it's nil + if player == nil then + self._passengers[i] = nil + self.driver_name = nil + end + else + if self._passengers[i] ~= nil then + --minetest.chat_send_all("pass: "..dump(self._passengers[i])) + --the rest of the passengers + if player then + if self._passenger_is_sit[i] == 0 then + local result_pos = get_result_pos(self, player, i) + local y_rot = 0 + if result_pos then + y_rot = result_pos.y -- the only field that returns a rotation + local new_pos = steampunk_blimp.copy_vector(self._passengers_base_pos[i]) + new_pos.x = new_pos.x - result_pos.z + new_pos.z = new_pos.z - result_pos.x + --minetest.chat_send_all(dump(new_pos)) + local pos_d = steampunk_blimp.navigate_deck(self._passengers_base_pos[i], new_pos, player) + --minetest.chat_send_all(dump(height)) + self._passengers_base_pos[i] = steampunk_blimp.copy_vector(pos_d) + self._passengers_base[i]:set_attach(self.object,'',self._passengers_base_pos[i],{x=0,y=0,z=0}) + end + --minetest.chat_send_all(dump(self._passengers_base_pos[i])) + player:set_attach(self._passengers_base[i], "", {x = 0, y = 0, z = 0}, {x = 0, y = y_rot, z = 0}) + else + local y_rot = 0 + if self._passenger_is_sit[i] == 1 then y_rot = 0 end + if self._passenger_is_sit[i] == 2 then y_rot = 90 end + if self._passenger_is_sit[i] == 3 then y_rot = 180 end + if self._passenger_is_sit[i] == 4 then y_rot = 270 end + player:set_attach(self._passengers_base[i], "", {x = 0, y = 3.6, z = 0}, {x = 0, y = y_rot, z = 0}) + airutils.sit(player) + end + end + end + end + end +end + + diff --git a/mods/sum_airship/.gitignore b/mods/sum_airship/.gitignore new file mode 100644 index 00000000..659a3dcb --- /dev/null +++ b/mods/sum_airship/.gitignore @@ -0,0 +1,46 @@ +*.blend1 +*.blend + +# ---> Lua +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.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/sum_airship/.vscode/launch.json b/mods/sum_airship/.vscode/launch.json new file mode 100644 index 00000000..b522eb7e --- /dev/null +++ b/mods/sum_airship/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Lua Interpreter", + "type": "lua-local", + "request": "launch", + "program": { + "lua": "lua", + "file": "${file}" + } + }, + { + "name": "Debug Custom Lua Environment", + "type": "lua-local", + "request": "launch", + "program": { + "command": "command" + }, + "args": [] + } + ] +} \ No newline at end of file diff --git a/mods/sum_airship/README.md b/mods/sum_airship/README.md new file mode 100644 index 00000000..ab69733f --- /dev/null +++ b/mods/sum_airship/README.md @@ -0,0 +1,42 @@ +[![ContentDB](https://content.luanti.org/packages/Sumianvoice/sum_airship/shields/downloads/)] + +# sum_airship + +Mod for [Luanti](https://luanti.org). You can download it from the in-game content manager, or [ContentDB](https://content.luanti.org/packages/Sumianvoice/sum_airship/). + +This mod adds an airship to Minetest. It should work in most games, but only Minetest Game, MineClone (VoxeLibre) and RePixture are supported for crafting recipes. Games based on those may still have crafting if they use the same items. Other games are not because the mod doesn't know what each game calls each item or node. Although, it shouldn't crash no matter what game you use, this is only for crafting recipes. + +**Controls:** Flies like a boat, WSAD controls with the addition of jump for up, sneak for down, and aux1 (sprint) to dismount. Does not require fuel but will go faster when given any type of coal. + +### Crafting + +#### MineClon\* (VoxeLibre) + +All items: wool: 27, string: 4, wood: 15, iron_ingot: 4 +9 Wool -> Canvas +3 Boats & 3 Iron Ingots -> Hull +3 Canvas, 1 Hull, 4 String & 1 Iron ingot -> Airship + +![MCL crafting.gif](crafting_mcl.gif) + +#### Minetest Game + +All items: wool: 27, string: 4, wood: 15, iron_ingot: 4 +9 Wool -> Canvas +3 Boats & 3 Iron Ingots -> Hull +3 Canvas, 1 Hull, 4 String & 1 Iron ingot -> Airship + +![MTG crafting.gif](crafting_mtg.gif) + +#### RePixture + +All items: wool: 27, fiber: 8, wood: varies depending on boat type, steel_ingot: 4 +9 group:fuzzy (wool or cotton bale) -> Canvas +3 Boats & 3 Steel Ingots -> Hull +3 Canvas, 1 Hull, 8 Fiber, 1 Steel Ingot -> Airship + +![RePixture crafting.gif](crafting_rp.gif) + +### Air Currents + +This optionally uses [sum_air_currents](https://content.luanti.org/packages/Sumianvoice/sum_air_currents/) to apply force based on the weather. diff --git a/mods/sum_airship/balloon.lua b/mods/sum_airship/balloon.lua new file mode 100644 index 00000000..49827839 --- /dev/null +++ b/mods/sum_airship/balloon.lua @@ -0,0 +1,440 @@ +local mod_name = minetest.get_current_modname() +local mod_path = minetest.get_modpath(mod_name) + +local me = { + lift = 4, + speed = 4, + fuel_time = 10, + speed_mult = 4, + i = {}, +} + +local ship = { + initial_properties = { + physical = true, + pointable = true, + collisionbox = {-0.6, -0.2, -0.6, 0.6, 0.4, 0.6}, + selectionbox = {-0.7, -0.35, -0.7, 0.7, 0.4, 0.7}, + hp_max = 3, + visual = "mesh", + backface_culling = true, + mesh = "sum_airship.b3d", + textures = {"sum_airship_texture.png"}, + }, + _animations = { + idle = {x= 10, y= 90}, + fly = {x= 91, y= 170}, + boost = {x= 91, y= 170}, + }, + _driver = nil, + _removed = false, + _flags = {}, + _itemstring = "sum_airship:boat", + _passenger = nil, + _vel = 0, + _regen_timer = 0, + _fuel = 0, +} + +local sounds = { + engine_idle = { + sound_name = "sum_airship_lip_trill", + gain = 1.4, + max_hear_distance = 10, + loop = false, + pitch = 0.75, + }, + engine_stop = { + sound_name = "sum_airship_lip_trill_end", + gain = 2.2, + max_hear_distance = 10, + loop = false, + pitch = 1, + }, + engine_boost = { + sound_name = "sum_airship_lip_trill", + gain = 40, + max_hear_distance = 10, + loop = false, + pitch = 1, + }, +} + +function me.sound_play(self, sound_obj, sound_instance) + sound_instance.handle = minetest.sound_play(sound_obj.sound_name, { + gain = sound_obj.gain, + max_hear_distance = sound_obj.max_hear_distance, + loop = sound_obj.loop, + pitch = sound_obj.pitch, + object = self.object, + }) + sound_instance.playing = true + sound_instance.time_elapsed = 0 +end + +function me.sound_stop(sound_instance) + if sound_instance.handle then + minetest.sound_stop(sound_instance.handle) + end + sound_instance.playing = false + sound_instance.time_elapsed = 0 + sound_instance.handle = nil +end + +function me.sound_countdown(self, dtime) + for _, sound in pairs(self._sounds) do + if sound.playing then + sound.time_elapsed = sound.time_elapsed + dtime + end + end +end + +function me.update_sound(self, dtime, forward) + me.sound_countdown(self, dtime) + + local is_thrust = (forward ~= 0) and self._driver + + if self._sounds.engine.time_elapsed > 2.1 + and self._sounds.engine.handle + and self._sounds.engine.playing then + me.sound_stop(self._sounds.engine) + end + if not self._sounds.engine.playing then + if self._fuel > 1 then + me.sound_play(self, sounds.engine_boost, self._sounds.engine) + elseif is_thrust then + me.sound_play(self, sounds.engine_idle, self._sounds.engine) + end + if self._fuel > 1 and self._sounds.engine_stop.playing then + me.sound_stop(self._sounds.engine_stop) + end + end + + if self._fuel <= 1 and self._sounds.engine.playing then + if self._fuel > 0 and not self._sounds.engine_stop.playing + and self._sounds.engine_stop.time_elapsed == 0 then + me.sound_play(self, sounds.engine_stop, self._sounds.engine_stop) + end + if not is_thrust + or (self._sounds.engine_stop.time_elapsed == 0 + and self._sounds.engine_stop.playing) then + me.sound_stop(self._sounds.engine) + end + end +end + + +function ship.on_activate(self, staticdata, dtime_s) + local data = minetest.deserialize(staticdata) + if type(data) == "table" then + self._vel = data.v + self._itemstring = data.itemstring + self._fuel = data.fuel + self._flags = data._flags + end + self.object:set_armor_groups({ + pierce=100, + slash=100, + blunt=100, + magic=100, + poison=100, + fleshy=100, + }) + self.object:set_animation(ship._animations.idle, 24) + self._sounds = { -- workaround for copy vs reference issue + engine = { + handle = nil, + gain = 0.1, + playing = false, + time_elapsed = 0, + }, + engine_stop = { + handle = nil, + gain = 0.1, + playing = false, + time_elapsed = 0, + }, + } +end + +function ship.get_staticdata(self) + return minetest.serialize({ + itemstring = self._itemstring, + _flags = self._flags, + v = self._vel, + fuel = self._fuel, + }) +end + +function me.attach(self, player) + if not (player and player:is_player()) then + return false + end + self._driver = player + self._driver:set_attach(self.object, "", + {x = 0, y = -0.0, z = -2}, {x = 0, y = 0, z = 0}) + self._driver:set_look_horizontal(self.object:get_yaw()) +end + +function me.detach(self) + if not self._driver then return false end + self._driver:set_detach() + self._driver = nil + return true +end + + +function ship.on_death(self, killer) + if killer and killer:is_player() + and not minetest.is_creative_enabled(killer:get_player_name()) then + local inv = killer:get_inventory() + inv:add_item("main", self._itemstring) + else + minetest.add_item(self.object:get_pos(), self._itemstring) + end + me.detach(self) + self._driver = nil +end + +function ship.on_rightclick(self, clicker) + local item = clicker:get_wielded_item() + local item_name = item:get_name() + if clicker and (item and item_name) + and (string.find(item_name, ":coal") + or string.find(item_name, ":charcoal")) then + if not minetest.is_creative_enabled(clicker:get_player_name()) then + item:take_item() + clicker:set_wielded_item(item) + end + self._fuel = self._fuel + me.fuel_time + me.sound_stop(self._sounds.engine) + minetest.sound_play("sum_airship_fire", { + gain = 1, + object = self.object, + }) + else + me.attach(self, clicker) + end +end + + +-- 10, 5, 5 +-- this system ensures collision kind of works with balloons. +-- does not include entity to entity collisions +local balloon = {} +balloon.offset = 6 +balloon.length = 3.5 +balloon.height = 2.5 +local balloon_nodes = {} +balloon_nodes[0] = { -- top + p = vector.new(0, balloon.offset + balloon.height, 0), + dir = vector.new(0, -5, 0),} +balloon_nodes[1] = { -- front + p = vector.new(0, balloon.offset, balloon.length), + dir = vector.new(0, -0.5, -1),} +balloon_nodes[2] = { -- back + p = vector.new(0, balloon.offset, -balloon.length), + dir = vector.new(0, 0, 1),} +balloon_nodes[3] = { -- left or right + p = vector.new(balloon.length, balloon.offset, 0), + dir = vector.new(-1, 0, 0),} +balloon_nodes[4] = { -- left or right + p = vector.new(-balloon.length, balloon.offset, 0), + dir = vector.new(1, 0, 0),} +-- diagonals +local vdiag = 0.7 +balloon_nodes[5] = { + p = vector.new(-balloon.length*vdiag, balloon.offset, -balloon.length*vdiag), + dir = vector.new(vdiag, 0, vdiag),} +balloon_nodes[6] = { + p = vector.new(balloon.length*vdiag, balloon.offset, -balloon.length*vdiag), + dir = vector.new(-vdiag, 0, vdiag),} +balloon_nodes[7] = { + p = vector.new(-balloon.length*vdiag, balloon.offset, balloon.length*vdiag), + dir = vector.new(vdiag, 0, -vdiag),} +balloon_nodes[8] = { + p = vector.new(balloon.length*vdiag, balloon.offset, balloon.length*vdiag), + dir = vector.new(-vdiag, 0, -vdiag),} + +function me.get_balloon_collide(self) + local force = vector.new() + local o = self.object:get_pos() + for _, check in pairs(balloon_nodes) do + local n = minetest.get_node(vector.add(check.p, o)) + if n and minetest.registered_nodes[n.name] + and (minetest.registered_nodes[n.name] or {}).walkable then + force = vector.add(force, check.dir) + end + end + return force +end + +me.chimney_dist = -0.8 +me.chimney_yaw = 0.13 +me.chimney_height = 1.5 +function me.get_chimney_pos(self) + local p = self.object:get_pos() + local yaw = self.object:get_yaw() + local ret = { + x = p.x + (me.chimney_dist * math.sin(-yaw + me.chimney_yaw)), + y = p.y + me.chimney_height, + z = p.z + (me.chimney_dist * math.cos(-yaw + me.chimney_yaw))} + return ret +end + +function ship.on_step(self, dtime, moveresult) + local exit = false + local pi = nil + -- allow to exit + if self._driver and self._driver:is_player() then + local name = self._driver:get_player_name() + pi = self._driver:get_player_control() + exit = pi.aux1 + end + if exit then + me.detach(self) + return false + end + + local climb = 0 + local right = 0 + local forward = 0 + local v = self.object:get_velocity() + local p = self.object:get_pos() + local node_below = minetest.get_node(vector.offset(p, 0, -0.8, 0)).name + local is_on_floor = (minetest.registered_nodes[node_below] or {}).walkable + local in_water = minetest.get_item_group(minetest.get_node(p).name, "liquid") ~= 0 + local on_water = (minetest.get_item_group(minetest.get_node(vector.offset(p, 0, -0.2, 0)).name, "liquid") ~= 0 and not in_water) + + local speedboost = 1 + if self._fuel > 0 then + self._fuel = self._fuel - dtime + speedboost = 3 + end + + if pi then + if pi.up then forward = 1 + elseif pi.down then forward = -1 end + if pi.jump then climb = 1 + elseif pi.sneak then climb = -1 end + if pi.right then right = 1 + elseif pi.left then right = -1 end + + local yaw = self.object:get_yaw() + local dir = minetest.yaw_to_dir(yaw) + self.object:set_yaw(yaw - right * dtime) + local added_vel = vector.multiply(dir, forward * dtime * me.speed * speedboost) + added_vel.y = added_vel.y + (climb * dtime * me.lift) + v = vector.add(v, added_vel) + end + + if self._driver then + local collide_force = me.get_balloon_collide(self) + if collide_force ~= vector.new() then + collide_force = vector.multiply(collide_force, 0.1) + v = vector.multiply(v, 0.95) + end + v = vector.add(v, collide_force) + end + + if not self._driver then + v.y = v.y - dtime + end + + if minetest.get_modpath("sum_air_currents") then + if self._driver or not is_on_floor then + local wind_vel = sum_air_currents.get_wind(p) + wind_vel = vector.multiply(wind_vel, dtime) + v = vector.add(wind_vel, v) + end + end + if in_water then + v.y = 1 + elseif on_water and not self._driver then + v.y = 0 + end + + if (not self._driver) and is_on_floor then + v.x = v.x * 0.8 + v.y = v.y * 0.95 + v.z = v.z * 0.8 + else + v.x = v.x * (0.98) + v.y = v.y * (0.98) + v.z = v.z * (0.98) + end + + local wind_vel = vector.new(0,0,0) + if minetest.get_modpath("sum_air_currents") ~= nil then + wind_vel = sum_air_currents.get_wind(p) + if self._driver or not is_on_floor then + v = vector.add(wind_vel, v) + end + end + + + self.object:set_velocity(v) + + me.update_sound(self, dtime, forward) + + + local is_thrust = self._driver and forward ~= 0 + + local chimney_pos = me.get_chimney_pos(self) + + local spread = 0.06 + if self._fuel > 0 or (math.random(0,100) > 80 and is_thrust) or math.random(0,100) > 95 then + minetest.add_particle({ + pos = vector.offset(chimney_pos, math.random(-1, 1)*spread, 0, math.random(-1, 1)*spread), + velocity = vector.add(wind_vel, {x=0, y=math.random(0.2*100,0.7*100)/100, z=0}), + expirationtime = math.random(0.5, 2), + size = math.random(0.1, 4), + collisiondetection = false, + vertical = false, + texture = "sum_airship_smoke.png", + }) + end + -- animations + if self._fuel > 0 then + self.object:set_animation(self._animations.boost, 25) + elseif is_thrust then + self.object:set_animation(self._animations.fly, 25) + else + self.object:set_animation(self._animations.idle, 25) + end +end + + +minetest.register_entity("sum_airship:boat", ship) + +minetest.register_craftitem("sum_airship:boat", { + description = "Airship", + inventory_image = "sum_airship.png", + groups = { vehicle = 1, airship = 1, transport = 1}, + on_place = function(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + local node = minetest.get_node(pointed_thing.under) + if placer and not placer:get_player_control().sneak then + local def = minetest.registered_nodes[node.name] + if def and def.on_rightclick then + return def.on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack + end + end + local pos = vector.offset(pointed_thing.above, 0, 0, 0) + local self = minetest.add_entity(pos, "sum_airship:boat"):get_luaentity() + if not minetest.is_creative_enabled(placer:get_player_name()) then + itemstack:take_item() + end + return itemstack + end, +}) + + +-- Support SilverSandstone's subtitles mod: +if minetest.get_modpath("subtitles") then + subtitles.register_description('sum_airship_lip_trill', 'Engine purring'); + subtitles.register_description('sum_airship_lip_trill_end', 'Engine sputtering'); + subtitles.register_description('sum_airship_fire', 'Engine stoked'); +end diff --git a/mods/sum_airship/crafting_mcl.gif b/mods/sum_airship/crafting_mcl.gif new file mode 100644 index 00000000..6b0051dd Binary files /dev/null and b/mods/sum_airship/crafting_mcl.gif differ diff --git a/mods/sum_airship/crafting_mcl.lua b/mods/sum_airship/crafting_mcl.lua new file mode 100644 index 00000000..3df41b40 --- /dev/null +++ b/mods/sum_airship/crafting_mcl.lua @@ -0,0 +1,28 @@ + +local w = "group:wool" +local b = "group:boat" +local m = "mcl_core:iron_ingot" +local s = "mcl_mobitems:string" +minetest.register_craft({ + output = "sum_airship:canvas_roll", + recipe = { + {w, w, w}, + {w, w, w}, + {w, w, w}, + }, +}) +minetest.register_craft({ + output = "sum_airship:hull", + recipe = { + {b, b, b}, + {m, m, m}, + }, +}) +minetest.register_craft({ + output = "sum_airship:boat", + recipe = { + {"sum_airship:canvas_roll","sum_airship:canvas_roll","sum_airship:canvas_roll",}, + {s, m, s,}, + {s, "sum_airship:hull", s,}, + }, +}) \ No newline at end of file diff --git a/mods/sum_airship/crafting_mtg.gif b/mods/sum_airship/crafting_mtg.gif new file mode 100644 index 00000000..58a2a892 Binary files /dev/null and b/mods/sum_airship/crafting_mtg.gif differ diff --git a/mods/sum_airship/crafting_mtg.lua b/mods/sum_airship/crafting_mtg.lua new file mode 100644 index 00000000..26618010 --- /dev/null +++ b/mods/sum_airship/crafting_mtg.lua @@ -0,0 +1,29 @@ + +local w = "group:wool" +if not minetest.get_modpath("farming") then w = "default:paper" end +local b = "boats:boat" +local m = "default:steel_ingot" +local s = "farming:string" +minetest.register_craft({ + output = "sum_airship:canvas_roll", + recipe = { + {w, w, w}, + {w, w, w}, + {w, w, w}, + }, +}) +minetest.register_craft({ + output = "sum_airship:hull", + recipe = { + {b, b, b}, + {m, m, m}, + }, +}) +minetest.register_craft({ + output = "sum_airship:boat", + recipe = { + {"sum_airship:canvas_roll","sum_airship:canvas_roll","sum_airship:canvas_roll",}, + {s, m, s,}, + {s, "sum_airship:hull", s,}, + }, +}) \ No newline at end of file diff --git a/mods/sum_airship/crafting_rp.gif b/mods/sum_airship/crafting_rp.gif new file mode 100644 index 00000000..7ac9a71b Binary files /dev/null and b/mods/sum_airship/crafting_rp.gif differ diff --git a/mods/sum_airship/crafting_rp.lua b/mods/sum_airship/crafting_rp.lua new file mode 100644 index 00000000..04b607ac --- /dev/null +++ b/mods/sum_airship/crafting_rp.lua @@ -0,0 +1,23 @@ + +crafting.register_craft({ + output = "sum_airship:canvas_roll", + items = { + "group:fuzzy 9" + } +}) +crafting.register_craft({ + output = "sum_airship:hull", + items = { + "group:boat 3", + "rp_default:ingot_steel 3" + } +}) +crafting.register_craft({ + output = "sum_airship:boat", + items = { + "sum_airship:canvas_roll 3", + "rp_default:ingot_steel 8", + "rp_default:ingot_steel", + "sum_airship:hull", + } +}) \ No newline at end of file diff --git a/mods/sum_airship/crafts.lua b/mods/sum_airship/crafts.lua new file mode 100644 index 00000000..f17d4ce4 --- /dev/null +++ b/mods/sum_airship/crafts.lua @@ -0,0 +1,33 @@ +local mod_name = minetest.get_current_modname() +local mod_path = minetest.get_modpath(mod_name) +local S = minetest.get_translator(minetest.get_current_modname()) + + +minetest.register_craftitem("sum_airship:canvas_roll", { + description = S("Canvas Roll"), + _doc_items_longdesc = S("Used in crafting airships."), + inventory_image = "sum_airship_canvas.png", + stack_max = 64, + groups = { craftitem = 1 }, +}) +minetest.register_craftitem("sum_airship:hull", { + description = S("Airship Hull"), + _doc_items_longdesc = S("Used in crafting airships."), + inventory_image = "sum_airship_hull.png", + stack_max = 1, + groups = { craftitem = 1 }, +}) + +if minetest.get_modpath("mcl_boats") +and minetest.get_modpath("mcl_wool") +and minetest.get_modpath("mcl_core") +and minetest.get_modpath("mcl_mobitems") then + dofile(mod_path .. "/crafting_mcl.lua") +elseif (minetest.get_modpath("rp_farming") +or minetest.get_modpath("rp_mobs_mobs")) +and minetest.get_modpath("rp_default") +and minetest.get_modpath("rp_crafting") then + dofile(mod_path .. "/crafting_rp.lua") +elseif minetest.get_modpath("default") then + dofile(mod_path .. "/crafting_mtg.lua") +end diff --git a/mods/sum_airship/init.lua b/mods/sum_airship/init.lua new file mode 100644 index 00000000..746c1acc --- /dev/null +++ b/mods/sum_airship/init.lua @@ -0,0 +1,5 @@ +local mod_name = minetest.get_current_modname() +local mod_path = minetest.get_modpath(mod_name) + +dofile(mod_path .. DIR_DELIM .. "balloon.lua") +dofile(mod_path .. DIR_DELIM .. "crafts.lua") diff --git a/mods/sum_airship/license.txt b/mods/sum_airship/license.txt new file mode 100644 index 00000000..a65a0586 --- /dev/null +++ b/mods/sum_airship/license.txt @@ -0,0 +1,23 @@ +Media contained within is under CC-BY-4.0. https://creativecommons.org/licenses/by/4.0/ + +MIT License + +Copyright (c) 2022 Sumi (info@sumianvoice.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/mods/sum_airship/mod.conf b/mods/sum_airship/mod.conf new file mode 100644 index 00000000..a56c1b38 --- /dev/null +++ b/mods/sum_airship/mod.conf @@ -0,0 +1,7 @@ +name = sum_airship +title = Airship (sum) +author = Sumianvoice +description = Adds a simple airship to Minetest Game, MineClone (VoxeLibre) and RePixture. +optional_depends = default, mcl_core, mcl_wool, mcl_mobitems, mcl_player, rp_crafting, rp_default + +release = 30601 diff --git a/mods/sum_airship/models/sum_airship.b3d b/mods/sum_airship/models/sum_airship.b3d new file mode 100644 index 00000000..48357e0a Binary files /dev/null and b/mods/sum_airship/models/sum_airship.b3d differ diff --git a/mods/sum_airship/sounds/sum_airship_fire.ogg b/mods/sum_airship/sounds/sum_airship_fire.ogg new file mode 100644 index 00000000..b7a9bb2c Binary files /dev/null and b/mods/sum_airship/sounds/sum_airship_fire.ogg differ diff --git a/mods/sum_airship/sounds/sum_airship_lip_trill.ogg b/mods/sum_airship/sounds/sum_airship_lip_trill.ogg new file mode 100644 index 00000000..d32b3452 Binary files /dev/null and b/mods/sum_airship/sounds/sum_airship_lip_trill.ogg differ diff --git a/mods/sum_airship/sounds/sum_airship_lip_trill_end.ogg b/mods/sum_airship/sounds/sum_airship_lip_trill_end.ogg new file mode 100644 index 00000000..16f996bd Binary files /dev/null and b/mods/sum_airship/sounds/sum_airship_lip_trill_end.ogg differ diff --git a/mods/sum_airship/textures/canvas.png b/mods/sum_airship/textures/canvas.png new file mode 100644 index 00000000..bd0e642f Binary files /dev/null and b/mods/sum_airship/textures/canvas.png differ diff --git a/mods/sum_airship/textures/sum_airship.png b/mods/sum_airship/textures/sum_airship.png new file mode 100644 index 00000000..d77d2e48 Binary files /dev/null and b/mods/sum_airship/textures/sum_airship.png differ diff --git a/mods/sum_airship/textures/sum_airship_canvas.png b/mods/sum_airship/textures/sum_airship_canvas.png new file mode 100644 index 00000000..9766060f Binary files /dev/null and b/mods/sum_airship/textures/sum_airship_canvas.png differ diff --git a/mods/sum_airship/textures/sum_airship_hull.png b/mods/sum_airship/textures/sum_airship_hull.png new file mode 100644 index 00000000..1a1649e1 Binary files /dev/null and b/mods/sum_airship/textures/sum_airship_hull.png differ diff --git a/mods/sum_airship/textures/sum_airship_smoke.png b/mods/sum_airship/textures/sum_airship_smoke.png new file mode 100644 index 00000000..e20f9e63 Binary files /dev/null and b/mods/sum_airship/textures/sum_airship_smoke.png differ diff --git a/mods/sum_airship/textures/sum_airship_texture.png b/mods/sum_airship/textures/sum_airship_texture.png new file mode 100644 index 00000000..9ecd9075 Binary files /dev/null and b/mods/sum_airship/textures/sum_airship_texture.png differ