diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6d8db4..314eee0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ### Requirements: -Luanti/Minetest 5.9.0+ +Luanti Experience with modding in Luanti @@ -16,8 +16,6 @@ Comments are encouraged, but don't go overboard. Make sure to test the code before opening the PR (or open a draft PR). -Ensure it supports the 5 most recent releases of Luanti, not including patch releases. - After you have finished, open a PR and **clearly explain what the PR does.** If it closes an issue, be sure to mention the issue. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..810c30f --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Simple Shooter Game (c) 2026 by a-bad-dev/user333_ and contributors is licensed under CC BY-SA 4.0. + +To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/ + +Simple Shooter Game uses textures and mods from various sources, see the mods/misc/* and mods/mtg/* subfolders for the respective licences. diff --git a/README.md b/README.md index ee82945..bb65c1c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # simple-shooter-game -A FPS game for the [Luanti Game Engine](https://luanti.org) (version 5.9.0+). Currently very WIP +A FPS game for the [Luanti game engine](https://luanti.org). Currently very WIP PRs welcome! diff --git a/TODO.txt b/TODO.txt index 707f087..2985267 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,13 +2,12 @@ Lines starting with "!" are high priority TODO: !- Add namespacing (`ssg.xyz()`) -!- Fix mapgen breaking when hosting from a dedicated server instead of the mainmenu (sometimes .-.) +!- Fix mapgen breaking when using non-singlenode mapgens (such as on dedicated servers, a workaround is to create it on a client and transfer it to the server) -- GUIs to do everything -- Improve the maps by adding structures -- Random loot spawning in chests in predefined places (inside houses) -!- Add a proper licence and give credit to the creators of the mods used -!- Make guns zoom in with a crosshair while rightclick is held +-- Make guns zoom in with a crosshair while rightclick is held -- Polish the game --- Rewrite the main mod from scratch with higher code standards +-- Rewrite the game from scratch with higher code standards (it's never going to happen, is it...) -- A scoring system with leaderboards -- Implement configurable teams diff --git a/game.conf b/game.conf index 845d60f..93e410e 100644 --- a/game.conf +++ b/game.conf @@ -1,3 +1,3 @@ -min_minetest_version = 5.9.0 -name = Simple Shooter Game +title = Simple Shooter Game allowed_mapgens = singlenode +author = a-bad-dev diff --git a/menu/background.1.png b/menu/background.1.png new file mode 100644 index 0000000..1da4ab3 Binary files /dev/null and b/menu/background.1.png differ diff --git a/menu/background.2.png b/menu/background.2.png new file mode 100644 index 0000000..db9ee7d Binary files /dev/null and b/menu/background.2.png differ diff --git a/mods/game/autoheal/init.lua b/mods/game/autoheal/init.lua index 874d51e..717461c 100644 --- a/mods/game/autoheal/init.lua +++ b/mods/game/autoheal/init.lua @@ -6,7 +6,7 @@ core.register_globalstep(function(dtime) if timer >= 10 then timer = 0 for _, player in pairs(core.get_connected_players()) do - if alive_players[player:get_player_name()] == "alive" then + if alive_players[player:get_player_name()] == "alive" and player:get_hp() > 0 then player:set_hp(math.min(player:get_hp() + 2, 20)) end end diff --git a/mods/game/chatcommands/init.lua b/mods/game/chatcommands/init.lua index 52d8afb..95cc0c9 100644 --- a/mods/game/chatcommands/init.lua +++ b/mods/game/chatcommands/init.lua @@ -12,8 +12,13 @@ core.register_chatcommand("start", { if match_state == "pre_match" or match_state == "post_match" or match_state == "in_progress" then return false, "-!- Match is already in progress!" end - - start_match(param) + + local sucess = start_match(param) + + if not map_data then + return false, "-!- Map not found!" + end + return true, "-!- Match started!" end }) @@ -23,7 +28,7 @@ core.register_chatcommand("stop", { privs = {match_manager = true}, description = "Terminate the match", func = function() - if match_state ~= "pre_match" and match_state ~= "post_match" and match_state ~= "not_started" then + if match_state ~= "post_match" and match_state ~= "not_started" then core.chat_send_all(core.colorize("red", "Match terminated.")) end_match() @@ -33,3 +38,16 @@ core.register_chatcommand("stop", { return false, "Match cannot be terminated at the moment." end }) + +core.register_chatcommand("maps", { + params = "", + privs = {match_manager = true}, + description = "List all maps", + func = function() + local list_string = "Available maps:\n" + for _, map in pairs(map_list) do + list_string = list_string .. map .. "\n" + end + return true, list_string .. "\nUse /start to start a match." + end +}) \ No newline at end of file diff --git a/mods/game/functions/init.lua b/mods/game/functions/init.lua index 92f06ec..0cbdf57 100644 --- a/mods/game/functions/init.lua +++ b/mods/game/functions/init.lua @@ -1,3 +1,17 @@ +match_start_jobs = {} +loading_tips = { + "Open the inventory to change class!", + "Short-range is good for small/dense maps!", + "Mid-range is good on maps with long tunnels or open areas!", + "Long-range is good for maps with really open and large areas!", + "Ambushing can be powerful assuming you're not found!", + "Players may sneak up on you from behind!", + "You *can't* be shot under water!", + "The Short-range class can one-shot from 6 to 7 nodes away!", + "Using torches may allow enemies to sneak up on you in the dark!", + "e s p i o n a g e", +} + -- Functions for SSG function make_player_invisible(player) -- Hide a player (pre-match and spectator) save_player_data(player) @@ -25,7 +39,7 @@ function give_player_items(player) -- Give the player their initial stuff for i = 1, #map_data.classes.class_2.initial_items do inv:add_item("main", map_data.classes.class_2.initial_items[i]) end - + elseif class == "3" then for i = 1, #map_data.classes.class_3.initial_items do inv:add_item("main", map_data.classes.class_3.initial_items[i]) @@ -45,34 +59,35 @@ function set_player_mode(player, mode) -- Set player mode (spectator, pre-match, }) load_player_data(player) - privs.noclip, privs.fast, privs.fly, privs.interact = false, false, false, true + privs.noclip, privs.fast, privs.fly, privs.interact, privs.debug = false, false, false, true, false playertag.set(player, playertag.TYPE_ENTITY, {a = 255, r = 255, g = 255, b = 255}) player:set_inventory_formspec([[ - size[8,4] - list[current_player;main;0,0;8,1;] - list[current_player;main;0,1.25;8,3;8] - listring[current_player;main] + size[8,4] + list[current_player;main;0,0;8,1;] + list[current_player;main;0,1.25;8,3;8] + listring[current_player;main] ]]) player:hud_set_flags({ hotbar = true, healthbar = true, breathbar = true, - }) + }) + elseif mode == "spectator" then - privs.noclip, privs.fast, privs.fly, privs.interact = true, true, true, false + privs.noclip, privs.fast, privs.fly, privs.interact, privs.debug = true, true, true, false, true make_player_invisible(player) - core.chat_send_player(player_name, core.colorize("blue", "You are now a spectator.")) + core.chat_send_player(player_name, core.colorize("#0574fc", "You are now a spectator.")) player:set_inventory_formspec([[ - size[8,4] - list[current_player;main;0,0;8,1;] - list[current_player;main;0,1.25;8,3;8] - listring[current_player;main] + size[8,4] + list[current_player;main;0,0;8,1;] + list[current_player;main;0,1.25;8,3;8] + listring[current_player;main] ]]) player:hud_set_flags({ @@ -87,17 +102,17 @@ function set_player_mode(player, mode) -- Set player mode (spectator, pre-match, make_player_invisible(player) player:set_inventory_formspec([[ - size[8,6] + size[8,6] label[3,0.1;Change class:] - button[0.3,1;2.5,1;class_sniper;]] .. map_data.classes.class_1.name .. [[] + button[0.3,1;2.5,1;class_sniper;]] .. map_data.classes.class_1.name .. [[] button[2.8,1;2.5,1;class_assault;]] .. map_data.classes.class_2.name .. [[] button[5.3,1;2.5,1;class_shotgun;]] .. map_data.classes.class_3.name .. [[] - list[current_player;main;0,2;8,1;] - list[current_player;main;0,3.25;8,3;8] - listring[current_player;main] + list[current_player;main;0,2;8,1;] + list[current_player;main;0,3.25;8,3;8] + listring[current_player;main] ]]) player:hud_set_flags({ @@ -115,70 +130,122 @@ function start_match(map) -- Start the match return end - set_match_state("pre_match") + place_map(map or "forest") -- default to forest if no map is specified - map_data = place_map(map or "forest") -- default to forest if no map is specified + if not map_data then + return nil + end - assert(loadstring(map_data.scripts.on_start or ""))() - - core.chat_send_all(core.colorize("green", string.format("Match about to start in %d seconds!\nOpen inventory to change class!", map_data.start_time))) - + local map_loading_images = {} for _, player in pairs(core.get_connected_players()) do set_player_mode(player, "pre_match") + + map_loading_images[player:get_player_name()] = { + loading = player:hud_add({ + type = "image", + position = {x=0.5, y=0.5}, + image_scale = 100, + text = "map_loading.png", + scale = {x=-100, y=-100}, + z_index = 1000, + }), + + info = player:hud_add({ + type = "text", + position = {x=0.5, y=0.7}, + text = loading_tips[math.random(1, #loading_tips)], + number = 0xFFFFFF, + z_index = 1000, + }) + } + give_player_items(player) - player:set_pos({x = map_data.spawn_x, y = map_data.spawn_y, z = map_data.spawn_z}) + player:set_pos(map_data.spawn) player:set_hp(20) end - for i = 10, 1, -1 do -- count down from 10 to 1 (yes you are free to set me on fire for this horrible solution) - core.after(map_data.start_time - 10 + i, function() - core.chat_send_all(core.colorize("green", string.format("Match starts in %d second%s.", 11 - i, 11 - i == 1 and "" or "s"))) -- <- RIP readability - end) - end - - core.after(map_data.start_time, function() - set_match_state("in_progress") - core.chat_send_all(core.colorize("green", "Match started!")) - - remove_barrier(map_data.size_x, map_data.barrier_level, map_data.size_z) - - alive_players = {} + core.after(3, function() + set_match_state("pre_match") for _, player in pairs(core.get_connected_players()) do - local player_name = player:get_player_name() - inv = player:get_inventory() - - inv:set_list("main", {}) - - give_player_items(player) - - player:set_properties({ - pointable = true, -- allow players to be killable after the match starts - }) - alive_players[player_name] = "alive" - - set_player_mode(player, "normal") + player:set_pos(map_data.spawn) + + for _, id in pairs(map_loading_images[player:get_player_name()]) do + player:hud_remove(id) + end end + + if map_data.on_start then + map_data.on_start() + end + + core.chat_send_all(core.colorize("#b011f9", string.format("Match about to start in %d seconds!\nOpen inventory to change class!", map_data.start_time))) + + match_start_jobs = {countdown = {}, map = nil} + for i = 10, 1, -1 do -- count down from 10 to 1 (yes you are free to set me on fire for this horrible solution) + table.insert(match_start_jobs.countdown, core.after(map_data.start_time - 10 + i, function() + core.chat_send_all(core.colorize("green", string.format("Match starts in %d second%s.", 11 - i, 11 - i == 1 and "" or "s"))) -- <- RIP readability + end)) + end + + match_start_jobs.map = core.after(map_data.start_time, function() + match_start_jobs = nil + set_match_state("in_progress") + core.chat_send_all(core.colorize("green", "Match started!")) + + remove_barrier() + + alive_players = {} + + for _, player in pairs(core.get_connected_players()) do + local player_name = player:get_player_name() + local inv = player:get_inventory() + + inv:set_list("main", {}) + + give_player_items(player) + + player:set_properties({ + pointable = true, -- allow players to be killable after the match starts + }) + alive_players[player_name] = "alive" + + set_player_mode(player, "normal") + end + end) end) end function end_match() -- End the match set_match_state("not_started") + if match_start_jobs then + match_start_jobs.map:cancel() + + for _, job in pairs(match_start_jobs.countdown) do + job:cancel() + end + + match_start_jobs = nil + end + + if map_data.on_end then + map_data.on_end() + end + for _, player in pairs(core.get_connected_players()) do player:set_pos(spawn_pos) player:get_inventory():set_list("main", {}) player:set_inventory_formspec([[ - size[8,4] - list[current_player;main;0,0;8,1;] - list[current_player;main;0,1.25;8,3;8] - listring[current_player;main] + size[8,4] + list[current_player;main;0,0;8,1;] + list[current_player;main;0,1.25;8,3;8] + listring[current_player;main] ]]) - player:set_properties({pointable = false}) set_player_mode(player, "normal") @@ -198,11 +265,11 @@ function save_player_data(player) -- Save the player's skin storing it in their if skins[1] == "blank.png" then return end - + player:get_meta():set_string("skin", core.serialize(skins)) end -function load_player_data(player) -- Load the player's skin stored in their metadata +function load_player_data(player) -- Load the player's skin stored in their metadata local skins = core.deserialize(player:get_meta():get_string("skin")) player:set_properties({ @@ -227,8 +294,6 @@ function kill_player(player, reason) -- Handle killed/disconnected players prope if #alive_player_names == 1 then local winner_name = alive_player_names[1] core.chat_send_all(core.colorize("green", winner_name .. " is the winner!")) - - assert(loadstring(map_data.scripts.on_end or ""))() set_match_state("post_match") diff --git a/mods/game/functions/textures/map_loading.png b/mods/game/functions/textures/map_loading.png new file mode 100644 index 0000000..f3695b8 Binary files /dev/null and b/mods/game/functions/textures/map_loading.png differ diff --git a/mods/game/main/init.lua b/mods/game/main/init.lua index 5ef686e..9cfc75e 100644 --- a/mods/game/main/init.lua +++ b/mods/game/main/init.lua @@ -38,11 +38,12 @@ core.register_on_joinplayer(function(player) player:get_inventory():set_list("main", {}) player:set_inventory_formspec([[ - size[8,4] - list[current_player;main;0,0;8,1;] - list[current_player;main;0,1.25;8,3;8] - listring[current_player;main] + size[8,4] + list[current_player;main;0,0;8,1;] + list[current_player;main;0,1.25;8,3;8] + listring[current_player;main] ]]) + player:set_properties({pointable = false}) player:hud_set_flags({ @@ -59,13 +60,13 @@ end) core.register_on_leaveplayer(function(player) local player_name = player:get_player_name() - + kill_player(player, "left the game") end) core.register_on_dieplayer(function(player) local player_name = player:get_player_name() - + kill_player(player, "died") end) @@ -75,9 +76,9 @@ core.register_on_respawnplayer(function(player) if match_state == "in_progress" or match_state == "post_match" then set_player_mode(player, "spectator") - player:set_pos({x = map_data.spawn_x, y = map_data.spawn_y, z = map_data.spawn_z}) + player:set_pos(map_data.spawn) player:get_inventory():set_list("main", {}) - + player:set_properties({pointable = false}) end diff --git a/mods/game/maps/README.md b/mods/game/maps/README.md index fa5c873..67d025e 100644 --- a/mods/game/maps/README.md +++ b/mods/game/maps/README.md @@ -6,6 +6,8 @@ Requirements: [Minetest/Luanti 5.0.0 or later](https://luanti.org/) +(you need at least 5.0.0 to make the map, but you need a more recent version to actually test it) + [Minetest Game](https://content.luanti.org/packages/Luanti/minetest_game/) [WorldEdit](https://content.luanti.org/packages/sfan5/worldedit/) @@ -32,23 +34,17 @@ Step 8: Open the `map.lua` file in a text editor and put the following content i ```lua local map_data = { name = "(Your map name here)", - size_x = (Size in the X direction of your map), - size_y = (Size in the Y direction of your map), - size_z = (Size in the Z direction of your map), + size = vector.new(Your map size) barrier_level = (Distance from the bottom of the map to the barrier), - spawn_x = nil, - spawn_y = nil, - spawn_z = nil, + spawn = vector.new(Your spawn coordinates relative to map position, can be set to nil) start_time = (Amount of time in seconds before the barrier is removed), - scripts = { - on_start = "(Lua script to be run after /start is run, leave blank unless you know what you are doing!)", - on_barrier_remove = "(Lua script to be run after the barrier is removed, leave blank unless you know what you are doing!)", - on_end = "(Lua script to be run after the match has ended, leave blank unless you know what you are doing!)" - }, + on_start = function(), + on_end = function(), + on_barrier_remove = function(), classes = { class_1 = { @@ -67,8 +63,6 @@ local map_data = { } } } - -return map_data ``` Step 9: Open Minetest/Luanti and create a new world with Simple Shooter Game. diff --git a/mods/game/maps/init.lua b/mods/game/maps/init.lua index 9e80bba..99e3913 100644 --- a/mods/game/maps/init.lua +++ b/mods/game/maps/init.lua @@ -1,18 +1,33 @@ -- Maps mod for SSG -function place_map(map) - local map_path = core.get_modpath("maps") .. "/maps/" - local map_data = dofile(map_path .. map .. "/map.lua") - local barrier_nodes = {} - - core.place_schematic({x=0, y=0, z=0}, map_path .. map .. "/map.mts", 0, nil, true) - - if map_data.spawn_x == nil or map_data.spawn_y == nil or map_data.spawn_z == nil then -- set a default spawnpoint if not set - map_data.spawn_x = map_data.size_x / 2 - map_data.spawn_y = map_data.barrier_level + 1 - map_data.spawn_z = map_data.size_z / 2 +local map_path = core.get_modpath("maps") .. "/maps/" +map_data = {} + +map_list = core.get_dir_list(map_path, true) +table.sort(map_list) + +function place_map(map) + local map_pos = vector.new(0, 0, 0) + + for i = 1, #map_list do + if map_list[i] == map then + map_pos = vector.new(1000 * (i - 1), 0, 0) + break + elseif i == #map_list then + return nil + end end + + map_data = dofile(map_path .. map .. "/map.lua") + map_data.pos = map_pos + core.place_schematic(map_pos, map_path .. map .. "/map.mts", 0, nil, true) + if not map_data.spawn then -- set a default spawnpoint if not set + map_data.spawn = vector.new(map_data.size.x / 2, map_data.barrier_level + 1, map_data.size.z / 2) + map_pos + else + map_data.spawn = map_data.spawn + map_pos + end + if map_data.start_time == nil or map_data.start_time <= 0 then map_data.start_time = 30 end @@ -23,32 +38,30 @@ function place_map(map) map_data.classes.class_2 = {} map_data.classes.class_3 = {} end - + if map_data.classes.class_1.initial_items == nil or map_data.classes.class_1.name == nil then - map_data.classes.class_1.initial_items = {"ctf_ranged:m200_loaded", "default:sword_stone", "ctf_ranged:ammo 100"} + map_data.classes.class_1.initial_items = {"ctf_ranged:m200_loaded", "default:sword_stone", "ctf_ranged:ammo 99"} map_data.classes.class_1.name = "Long-range" end if map_data.classes.class_2.initial_items == nil or map_data.classes.class_2.name == nil then - map_data.classes.class_2.initial_items = {"ctf_ranged:ak47_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 100"} + map_data.classes.class_2.initial_items = {"ctf_ranged:ak47_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99"} map_data.classes.class_2.name = "Mid-range" end if map_data.classes.class_3.initial_items == nil or map_data.classes.class_3.name == nil then - map_data.classes.class_3.initial_items = {"ctf_ranged:benelli_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 100"} + map_data.classes.class_3.initial_items = {"ctf_ranged:benelli_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99"} map_data.classes.class_3.name = "Short-range" end - - - return map_data end -function remove_barrier(x, y, z) - for node_x = 1, x - 2 do - for node_z = 1, z - 2 do - core.set_node({x = node_x, y = y - 1, z = node_z}, {name = "air"}) -- account for the fact that lua counts starting at 1... i think.... whatever, it works \_('_')_/ - end +function remove_barrier() + for _, player in pairs(core.get_connected_players()) do + local pos = player:get_pos() + player:set_pos({x=pos.x, y=map_data.barrier_level - 3.5, z=pos.z}) + end + + if map_data.on_barrier_remove then + map_data.on_barrier_remove() end - assert(loadstring(map_data.scripts.on_barrier_remove or ""))() - return "" end diff --git a/mods/game/maps/maps/1v1/map.lua b/mods/game/maps/maps/1v1/map.lua new file mode 100644 index 0000000..ad33db0 --- /dev/null +++ b/mods/game/maps/maps/1v1/map.lua @@ -0,0 +1,14 @@ +return { + name = "1v1", + size = vector.new(41, 31, 38), + + barrier_level = 27, + + spawn = nil, + + start_time = 15, + + on_start = nil, + on_end = nil, + on_barrier_remove = nil, +} diff --git a/mods/game/maps/maps/1v1/map.mts b/mods/game/maps/maps/1v1/map.mts new file mode 100644 index 0000000..135902b Binary files /dev/null and b/mods/game/maps/maps/1v1/map.mts differ diff --git a/mods/game/maps/maps/forest-2/map.lua b/mods/game/maps/maps/forest-2/map.lua index 6022078..ca9276f 100644 --- a/mods/game/maps/maps/forest-2/map.lua +++ b/mods/game/maps/maps/forest-2/map.lua @@ -1,22 +1,14 @@ -local map_data = { +return { name = "forest-2", - size_x = 189, - size_y = 71, - size_z = 102, - + size = vector.new(189, 71, 102), + barrier_level = 67, -- <- Y level of the barrier - spawn_x = nil, - spawn_y = nil, - spawn_z = nil, + spawn = nil, start_time = 45, - scripts = { -- "temporary" hack to ensure there's nothing on top of the map - on_start = "for x=0, 188 do\nfor y=0, 4 do\nfor z=0, 101 do\ncore.set_node({x=x,y=71+y,z=z}, {name=\"air\"})\nend\nend\nend", - on_barrier_remove = "", - on_end = "" - } + on_start = nil, + on_end = nil, + on_barrier_remove = nil, } - -return map_data diff --git a/mods/game/maps/maps/forest-3/map.lua b/mods/game/maps/maps/forest-3/map.lua index 4b90dd2..8520f1c 100644 --- a/mods/game/maps/maps/forest-3/map.lua +++ b/mods/game/maps/maps/forest-3/map.lua @@ -1,22 +1,14 @@ -local map_data = { +return { name = "forest-3", - size_x = 537, - size_y = 117, - size_z = 244, + size = vector.new(537, 117, 244), barrier_level = 113, -- <- Y level of the barrier - spawn_x = nil, - spawn_y = nil, - spawn_z = nil, + spawn = nil, start_time = 60, - scripts = { - on_start = "", - on_barrier_remove = "", - on_end = "" - } + on_start = nil, + on_end = nil, + on_barrier_remove = nil, } - -return map_data diff --git a/mods/game/maps/maps/forest-4/map.lua b/mods/game/maps/maps/forest-4/map.lua new file mode 100644 index 0000000..f0b366a --- /dev/null +++ b/mods/game/maps/maps/forest-4/map.lua @@ -0,0 +1,31 @@ +return { + name = "forest-4", + size = vector.new(190, 69, 155), + + barrier_level = 65, + + spawn = nil, + + start_time = 30, + + on_start = nil, + on_end = nil, + on_barrier_remove = nil, + + classes = { + class_1 = { + name = "Long-range", + initial_items = {"ctf_ranged:m200_loaded", "default:sword_stone", "ctf_ranged:ammo 99", "default:torch 1"} + }, + + class_2 = { + name = "Mid-ranged", + initial_items = {"ctf_ranged:ak47_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99", "default:torch 1"} + }, + + class_3 = { + name = "Short-range", + initial_items = {"ctf_ranged:benelli_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99", "default:torch 1"} + } + } +} diff --git a/mods/game/maps/maps/forest-4/map.mts b/mods/game/maps/maps/forest-4/map.mts new file mode 100644 index 0000000..349147d Binary files /dev/null and b/mods/game/maps/maps/forest-4/map.mts differ diff --git a/mods/game/maps/maps/forest/map.lua b/mods/game/maps/maps/forest/map.lua index 66cff30..719b6c6 100644 --- a/mods/game/maps/maps/forest/map.lua +++ b/mods/game/maps/maps/forest/map.lua @@ -1,22 +1,31 @@ -local map_data = { +return { name = "forest", - size_x = 155, - size_y = 53, - size_z = 147, - + size = vector.new(155, 53, 147), + barrier_level = 49, -- <- Y level of the barrier - spawn_x = nil, - spawn_y = nil, - spawn_z = nil, + spawn = nil, start_time = 30, - scripts = { -- "temporary" hack to ensure there's nothing on top of the map - on_start = "for x=0, 154 do\nfor y=0, 16 do\nfor z=0, 146 do\ncore.set_node({x=x,y=53+y,z=z}, {name=\"air\"})\nend\nend\nend", - on_barrier_remove = "", - on_end = "" + on_start = nil, + on_end = nil, + on_barrier_remove = nil, + + classes = { + class_1 = { + name = "Long-range", + initial_items = {"ctf_ranged:m200_loaded", "default:sword_stone", "ctf_ranged:ammo 99", "default:torch 1"} + }, + + class_2 = { + name = "Mid-ranged", + initial_items = {"ctf_ranged:ak47_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99", "default:torch 1"} + }, + + class_3 = { + name = "Short-range", + initial_items = {"ctf_ranged:benelli_loaded", "ctf_ranged:glock17_loaded", "ctf_ranged:ammo 99", "default:torch 1"} + } } } - -return map_data diff --git a/mods/game/maps/maps/forest/map.mts b/mods/game/maps/maps/forest/map.mts index 38555d5..8cf8cc1 100644 Binary files a/mods/game/maps/maps/forest/map.mts and b/mods/game/maps/maps/forest/map.mts differ diff --git a/mods/game/maps/maps/mini-map/map.lua b/mods/game/maps/maps/mini-map/map.lua new file mode 100644 index 0000000..12f0ace --- /dev/null +++ b/mods/game/maps/maps/mini-map/map.lua @@ -0,0 +1,24 @@ +return { + name = "mini-map", + size = vector.new(8, 19, 8), + + -- This is a ridiculous hack to prevent players from teleporting into the ground.. + barrier_level = 19, + + spawn = vector.new(4, 15, 4), + + start_time = 15, + + on_start = nil, + on_end = nil, + on_barrier_remove = function() + local pos = map_data.pos + local size = map_data.size + pos + + for x = pos.x + 1, size.x - 2 do + for z = pos.z + 1, size.z - 2 do + core.set_node(vector.new(x, 14, z), {name = "air"}) + end + end + end, +} diff --git a/mods/game/maps/maps/mini-map/map.mts b/mods/game/maps/maps/mini-map/map.mts new file mode 100644 index 0000000..1627521 Binary files /dev/null and b/mods/game/maps/maps/mini-map/map.mts differ diff --git a/mods/game/maps/maps/pine/map.lua b/mods/game/maps/maps/pine/map.lua index 108fbb1..cd1a7bd 100644 --- a/mods/game/maps/maps/pine/map.lua +++ b/mods/game/maps/maps/pine/map.lua @@ -1,22 +1,14 @@ -local map_data = { +return { name = "pine", - size_x = 111, - size_y = 64, - size_z = 107, + size = vector.new(111, 64, 107), barrier_level = 60, -- <- Y level of the barrier - spawn_x = nil, - spawn_y = nil, - spawn_z = nil, + spawn = nil, start_time = 30, - scripts = { - on_start = "", - on_barrier_remove = "", - on_end = "" - } + on_start = nil, + on_end = nil, + on_barrier_remove = nil, } - -return map_data diff --git a/mods/game/maps/maps/savanna/map.lua b/mods/game/maps/maps/savanna/map.lua index 1bb8814..1263863 100644 --- a/mods/game/maps/maps/savanna/map.lua +++ b/mods/game/maps/maps/savanna/map.lua @@ -1,22 +1,14 @@ -local map_data = { +return { name = "savanna", - size_x = 341, - size_y = 83, - size_z = 188, + size = vector.new(341, 83, 188), barrier_level = 79, -- <- Y level of the barrier - spawn_x = nil, - spawn_y = nil, - spawn_z = nil, + spawn = nil, start_time = 45, - scripts = { - on_start = "", - on_barrier_remove = "", - on_end = "" - } + on_start = nil, + on_end = nil, + on_barrier_remove = nil, } - -return map_data diff --git a/mods/misc/ctf_guns/ctf_ranged/custom_controls.lua b/mods/misc/ctf_guns/ctf_ranged/custom_controls.lua index 2b0d71c..5f7be02 100644 --- a/mods/misc/ctf_guns/ctf_ranged/custom_controls.lua +++ b/mods/misc/ctf_guns/ctf_ranged/custom_controls.lua @@ -50,7 +50,7 @@ end) minetest.register_on_joinplayer(function(player) player_scope_huds[player:get_player_name()] = player:hud_add({ - hud_elem_type = "image", + type = "image", alignment = { x=0.0, y=0.0 }, position = {x = 0.5, y = 0.5}, scale = { x=2, y=2 }, diff --git a/mods/misc/ctf_guns/ctf_ranged/wep_defns.lua b/mods/misc/ctf_guns/ctf_ranged/wep_defns.lua index 82e4f59..0fbbd39 100644 --- a/mods/misc/ctf_guns/ctf_ranged/wep_defns.lua +++ b/mods/misc/ctf_guns/ctf_ranged/wep_defns.lua @@ -254,7 +254,7 @@ ctf_ranged.simple_register_gun("ctf_ranged:m200", { fire_sound = "ctf_ranged_m16fire", rounds = 5, scope_zoom=10, - range = 200, + range = 2000, damage = 15, fire_interval = 2.0, liquid_travel_dist = 4, diff --git a/mods/misc/ctf_guns/darkness_nerf/init.lua b/mods/misc/ctf_guns/darkness_nerf/init.lua deleted file mode 100644 index c320e0a..0000000 --- a/mods/misc/ctf_guns/darkness_nerf/init.lua +++ /dev/null @@ -1,27 +0,0 @@ - --- Makes players glow -minetest.register_on_joinplayer(function(player) - player:set_properties({glow = 3}) -end) - -local MIN_GLOW = 8 - --- Makes dropped items glow -minetest.register_on_mods_loaded(function() - local itemdef = minetest.registered_entities["__builtin:item"] - local old_set_item = itemdef.set_item - - itemdef.set_item = function(self, itemstring) - old_set_item(self, itemstring) - local iname = itemstring or self.itemstring - iname = ItemStack(iname):get_name() - - if not minetest.registered_items[iname] or (minetest.registered_items[iname].light_source or 0) < MIN_GLOW then - self.object:set_properties({glow = MIN_GLOW}) - else - self.object:set_properties({glow = minetest.registered_items[iname].light_source}) - end - end - - minetest.register_entity(":__builtin:item", itemdef) -end) diff --git a/mods/misc/ctf_guns/darkness_nerf/mod.conf b/mods/misc/ctf_guns/darkness_nerf/mod.conf deleted file mode 100644 index 12f9a7d..0000000 --- a/mods/misc/ctf_guns/darkness_nerf/mod.conf +++ /dev/null @@ -1,2 +0,0 @@ -name = darkness_nerf -depends = default diff --git a/mods/misc/playertag/init.lua b/mods/misc/playertag/init.lua index 3c06f3c..829d0cc 100644 --- a/mods/misc/playertag/init.lua +++ b/mods/misc/playertag/init.lua @@ -130,7 +130,7 @@ local function update(player, settings) if settings.type == TYPE_BUILTIN then player:set_nametag_attributes({ - color = settings.color or {a=255, r=255, g=255, b=255}, + color = {a=settings.color.a or 255, r=settings.color.r or 255, g=settings.color.g or 255, b=settings.color.b or 255}, bgcolor = {a=0, r=0, g=0, b=0}, }) elseif settings.type == TYPE_ENTITY then @@ -162,30 +162,32 @@ function playertag.get_all() end minetest.register_entity("playertag:tag", { - visual = "sprite", - visual_size = {x=2.16, y=0.18, z=2.16}, --{x=1.44, y=0.12, z=1.44}, - textures = {"blank.png"}, - collisionbox = { 0, -0.2, 0, 0, -0.2, 0 }, - physical = false, - makes_footstep_sound = false, - backface_culling = false, - static_save = false, - pointable = false, - on_punch = function() return true end, - on_deactivate = function(self, removal) - if not removal then - local attachmentInfo = self.object:get_attach() - local player = nil - if attachmentInfo then - player = attachmentInfo.parent - end + initial_properties = { + visual = "sprite", + visual_size = {x=2.16, y=0.18, z=2.16}, --{x=1.44, y=0.12, z=1.44}, + textures = {"blank.png"}, + collisionbox = { 0, -0.2, 0, 0, -0.2, 0 }, + physical = false, + makes_footstep_sound = false, + backface_culling = false, + static_save = false, + pointable = false, + on_punch = function() return true end, + on_deactivate = function(self, removal) + if not removal then + local attachmentInfo = self.object:get_attach() + local player = nil + if attachmentInfo then + player = attachmentInfo.parent + end - if player and player:is_player() then - minetest.log("action", "Playertag for player "..player:get_player_name().." unloaded. Re-adding...") - update(player, players[player:get_player_name()]) + if player and player:is_player() then + minetest.log("action", "Playertag for player "..player:get_player_name().." unloaded. Re-adding...") + update(player, players[player:get_player_name()]) + end end end - end + } }) minetest.register_on_joinplayer(function(player) diff --git a/mods/misc/sprint/depends.txt b/mods/misc/sprint/depends.txt deleted file mode 100644 index 3e1d5c2..0000000 --- a/mods/misc/sprint/depends.txt +++ /dev/null @@ -1 +0,0 @@ -hudbars? diff --git a/mods/misc/sprint/esprint.lua b/mods/misc/sprint/esprint.lua index 87797ec..861b9c1 100644 --- a/mods/misc/sprint/esprint.lua +++ b/mods/misc/sprint/esprint.lua @@ -37,7 +37,7 @@ minetest.register_on_joinplayer(function(player) hb.init_hudbar(player, "sprint") else players[playerName].hud = player:hud_add({ - hud_elem_type = "statbar", + type = "statbar", position = {x=0.5,y=1}, size = {x=24, y=24}, text = "sprint_stamina_icon.png", @@ -70,14 +70,14 @@ minetest.register_globalstep(function(dtime) --If the player is sprinting, create particles behind him/her if playerInfo["sprinting"] == true and gameTime % 0.1 == 0 then local numParticles = math.random(1, 2) - local playerPos = player:getpos() + local playerPos = player:get_pos() local playerNode = minetest.get_node({x=playerPos["x"], y=playerPos["y"]-1, z=playerPos["z"]}) if playerNode["name"] ~= "air" then for i=1, numParticles, 1 do minetest.add_particle({ pos = {x=playerPos["x"]+math.random(-1,1)*math.random()/2,y=playerPos["y"]+0.1,z=playerPos["z"]+math.random(-1,1)*math.random()/2}, - vel = {x=0, y=5, z=0}, - acc = {x=0, y=-13, z=0}, + velocity = {x=0, y=5, z=0}, + acceleration = {x=0, y=-13, z=0}, expirationtime = math.random(), size = math.random()+0.5, collisiondetection = true, diff --git a/mods/misc/sprint/mod.conf b/mods/misc/sprint/mod.conf new file mode 100644 index 0000000..3e3c1a2 --- /dev/null +++ b/mods/misc/sprint/mod.conf @@ -0,0 +1,2 @@ +name = sprint +description = Slightly improved version of GunshipPenguin's sprint mod \ No newline at end of file diff --git a/mods/misc/wielded_light/LICENSE b/mods/misc/wielded_light/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/mods/misc/wielded_light/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/mods/misc/wielded_light/README.md b/mods/misc/wielded_light/README.md new file mode 100644 index 0000000..59b79cb --- /dev/null +++ b/mods/misc/wielded_light/README.md @@ -0,0 +1,24 @@ +# wielded_light mod for Minetest + +Idea taken from torches_wieldlight in https://github.com/minetest-mods/torches, but written from scratch and usable for all shining items. + +![Screenshot](https://github.com/bell07/minetest-wielded_light/raw/master/screenshot.png) + +All bright nodes with light value > 2 lighten the player environment if wielded, with value fewer by 2. (Torch 13->11 for example) + +Dependencies: none + +License: [GPL-3](https://github.com/bell07/minetest-wielded_light/blob/master/LICENSE) + + +Shining API: + +`function wielded_light.update_light(pos, light_level)` +Enable or update the shining at pos with light_level for 0.6 seconds. Can be used in any on_step call to get other entitys shining for example + + +`wielded_light.register_item_light(itemname, light_level)` +Override or set custom light level to an item. This does not change the item/node definition, just the lighting in this mod. + +`function wielded_light.update_light_by_item(stack, pos)` +Update light at pos using item shining settings -from registered item_light or from item definition diff --git a/mods/misc/wielded_light/init.lua b/mods/misc/wielded_light/init.lua new file mode 100644 index 0000000..f36789f --- /dev/null +++ b/mods/misc/wielded_light/init.lua @@ -0,0 +1,646 @@ +local mod_name = minetest.get_current_modname() + +-- Node replacements that emit light +-- Sets of lighting_node={ node=original_node, level=light_level } +local lighting_nodes = {} + +-- The nodes that can be replaced with lighting nodes +-- Sets of original_node={ [1]=lighting_node_1, [2]=lighting_node_2, ... } +local lightable_nodes = {} + +-- Prefixes used for each node so we can avoid overlap +-- Pairs of prefix=original_node +local lighting_prefixes = {} + +-- node_name=true pairs of lightable nodes that are liquids and can flood some light sources +local lightable_liquids = {} + +-- How often will the positions of lights be recalculated +local update_interval = 0.2 + +-- How long until a previously lit node should be updated - reduces flicker +local removal_delay = update_interval * 0.5 + +-- How often will a node attempt to check itself for deletion +local cleanup_interval = update_interval * 3 + +-- How far in the future will the position be projected based on the velocity +local velocity_projection = update_interval * 1 + +-- How many light levels should an item held in the hand be reduced by, compared to the placed node +-- does not apply to manually registered light levels +local level_delta = 2 + +-- item=light_level pairs of registered wielded lights +local shiny_items = {} + +-- List of custom callbacks for each update step +local update_callbacks = {} +local update_player_callbacks = {} + +-- position={id=light_level} sets of known about light sources and their levels by position +local active_lights = {} + +--[[ Sets of entities being tracked, in the form: +entity_id = { + obj = entity, + items = { + category_id..entity_id = { + level = light_level, + item? = item_name + } + }, + update = true | false, + pos? = position_vector, + offset? = offset_vector, +} +]] +local tracked_entities = {} + +-- position=true pairs of positions that need to be recaculated this update step +local light_recalcs = {} + +--[[ + Using 2-digit hex codes for categories + Starts at 00, ends at FF + This makes it easier extract `uid` from `cat_id..uid` by slicing off 2 characters + The category ID must be of a fixed length (2 characters) +]] +local cat_id = 0 +local cat_codes = {} +local function get_light_category_id(cat) + -- If the category id does not already exist generate a new one + if not cat_codes[cat] then + if cat_id >= 256 then + error("Wielded item category limit exceeded, maximum 256 wield categories") + end + local code = string.format("%02x", cat_id) + cat_id = cat_id+1 + cat_codes[cat] = code + end + -- If the category id does exist, return it + return cat_codes[cat] +end + +-- Log an error coming from this mod +local function error_log(message, ...) + minetest.log("error", "[Wielded Light] " .. (message:format(...))) +end + +-- Is a node lightable and a liquid capable of flooding some light sources +local function is_lightable_liquid(pos) + local node = minetest.get_node_or_nil(pos) + if not node then return end + return lightable_liquids[node.name] +end + +-- Check if an entity instance still exists in the world +local function is_entity_valid(entity) + return entity and (entity.obj:is_player() or (entity.obj:get_luaentity() and entity.obj:get_luaentity().name) or false) +end + +-- Check whether a node was registered by the wield_light mod +local function is_wieldlight_node(pos_vec) + local name = string.sub(minetest.get_node(pos_vec).name, 1, #mod_name) + return name == mod_name +end + +-- Get the projected position of an entity based on its velocity, rounded to the nearest block +local function entity_pos(obj, offset) + local velocity + if (minetest.features.direct_velocity_on_players or not obj:is_player()) and obj.get_velocity then + velocity = obj:get_velocity() + else + velocity = obj:get_player_velocity() + end + + return wielded_light.get_light_position( + vector.round( + vector.add( + vector.add( + offset or { x=0, y=0, z=0 }, + obj:get_pos() + ), + vector.multiply( + velocity or { x=0, y=0, z=0 }, + velocity_projection + ) + ) + ) + ) +end + +-- Add light to active light list and mark position for update +local function add_light(pos, id, light_level) + if not active_lights[pos] then + active_lights[pos] = {} + end + if active_lights[pos][id] ~= light_level then + -- minetest.log("error", "add "..id.." "..pos.." "..tostring(light_level)) + active_lights[pos][id] = light_level + light_recalcs[pos] = true + end +end + +-- Remove light from active light list and mark position for update +local function remove_light(pos, id) + if not active_lights[pos] then return end + -- minetest.log("error", "rem "..id.." "..pos) + active_lights[pos][id] = nil + minetest.after(removal_delay, function () + light_recalcs[pos] = true + end) +end + +-- Track an entity's position and update its light, will be called on every update step +local function update_entity(entity) + local pos = entity_pos(entity.obj, entity.offset) + local pos_str = pos and minetest.pos_to_string(pos) + + -- If the position has changed, remove the old light and mark the entity for update + if entity.pos and pos_str ~= entity.pos then + entity.update = true + for id,_ in pairs(entity.items) do + remove_light(entity.pos, id) + end + end + + -- Update the recorded position + entity.pos = pos_str + + -- If the position is still loaded, pump the timer up so it doesn't get removed + if pos then + -- If the entity is marked for an update, add the light in the position if it emits light + if entity.update then + for id, item in pairs(entity.items) do + if item.level > 0 and not (item.floodable and is_lightable_liquid(pos)) then + add_light(pos_str, id, item.level) + else + remove_light(pos_str, id) + end + end + end + end + if active_lights[pos_str] then + if is_wieldlight_node(pos) then + minetest.get_node_timer(pos):start(cleanup_interval) + end + end + entity.update = false +end + + +-- Save the original nodes timer if it has one +local function save_timer(pos_vec) + local timer = minetest.get_node_timer(pos_vec) + if timer:is_started() then + local meta = minetest.get_meta(pos_vec) + meta:set_float("saved_timer_timeout", timer:get_timeout()) + meta:set_float("saved_timer_elapsed", timer:get_elapsed()) + end +end + +-- Restore the original nodes timer if it had one +local function restore_timer(pos_vec) + local meta = minetest.get_meta(pos_vec) + local timeout = meta:get_float("saved_timer_timeout") + if timeout > 0 then + local elapsed = meta:get_float("saved_timer_elapsed") + local timer = minetest.get_node_timer(pos_vec) + timer:set(timeout, elapsed) + meta:set_string("saved_timer_timeout","") + meta:set_string("saved_timer_elapsed","") + end +end + +-- Replace a lighting node with its original counterpart +local function reset_lighting_node(pos) + local existing_node = minetest.get_node(pos) + local lighting_node = wielded_light.get_lighting_node(existing_node.name) + if not lighting_node then + return + end + minetest.swap_node(pos, { name = lighting_node.node,param2 = existing_node.param2 }) + restore_timer(pos) +end + +-- Will be run once the node timer expires +local function cleanup_timer_callback(pos, elapsed) + local pos_str = minetest.pos_to_string(pos) + local lights = active_lights[pos_str] + -- If no active lights for this position, remove itself + if not lights then + reset_lighting_node(pos) + else + -- Clean up any tracked entities for this position that no longer exist + for id,_ in pairs(lights) do + local uid = string.sub(id,3) + local entity = tracked_entities[uid] + if not is_entity_valid(entity) then + remove_light(pos_str, id) + end + end + minetest.get_node_timer(pos):start(cleanup_interval) + end +end + + +-- Recalculate the total light level for a given position and update the light level there +local function recalc_light(pos) + -- If not in active lights list we can't do anything + if not active_lights[pos] then return end + + -- Calculate the light level of the node + local any_light = false + local max_light = 0 + for id, light_level in pairs(active_lights[pos]) do + any_light = true + if light_level > max_light then + max_light = light_level + end + end + + -- Convert the position back to a vector + local pos_vec = minetest.string_to_pos(pos) + + -- If no items in this position, delete it from the list and remove any light node + if not any_light then + active_lights[pos] = nil + reset_lighting_node(pos_vec) + return + end + + -- If no light in this position remove any light node + if max_light == 0 then + reset_lighting_node(pos_vec) + return + end + + -- Limit the light level + max_light = math.min(max_light, minetest.LIGHT_MAX) + + -- Get the current light level in this position + local existing_node = minetest.get_node(pos_vec) + local name = existing_node.name + local old_value = wielded_light.level_of_lighting_node(name) or 0 + + -- If the light level has changed, set the coresponding light node and initiate the cleanup timer + if old_value ~= max_light then + local node_name + if lightable_nodes[name] then + node_name = name + elseif lighting_nodes[name] then + node_name = lighting_nodes[name].node + end + if node_name then + if not is_wieldlight_node(pos_vec) then + save_timer(pos_vec) + end + + minetest.swap_node(pos_vec, { + name = lightable_nodes[node_name][max_light], + param2 = existing_node.param2 + }) + minetest.get_node_timer(pos_vec):start(cleanup_interval) + else + active_lights[pos] = nil + end + end +end + +local timer = 0 +-- Will be run on every global step +local function global_timer_callback(dtime) + -- Only run once per update interval, global step will be called much more often than that + timer = timer + dtime; + if timer < update_interval then + return + end + timer = 0 + + -- Run all custom player callbacks for each player + local connected_players = minetest.get_connected_players() + for _,callback in pairs(update_player_callbacks) do + for _, player in pairs(connected_players) do + callback(player) + end + end + + -- Run all custom callbacks + for _,callback in pairs(update_callbacks) do + callback() + end + + -- Look at each tracked entity and update its position + for uid, entity in pairs(tracked_entities) do + if is_entity_valid(entity) then + update_entity(entity) + else + -- If the entity no longer exists, stop tracking it + tracked_entities[uid] = nil + end + end + + -- Recalculate light levels + for pos,_ in pairs(light_recalcs) do + recalc_light(pos) + end + light_recalcs = {} +end + +--- Shining API --- +wielded_light = {} + +-- Registers a callback to be called every time the update interval is passed +function wielded_light.register_lightstep(callback) + table.insert(update_callbacks, callback) +end + +-- Registers a callback to be called for each player every time the update interval is passed +function wielded_light.register_player_lightstep(callback) + table.insert(update_player_callbacks, callback) +end + +-- Returns the node name for a given light level +function wielded_light.lighting_node_of_level(light_level, prefix) + return mod_name..":"..(prefix or "")..light_level +end + +-- Gets the light level for a given node name, inverse of lighting_node_of_level +function wielded_light.level_of_lighting_node(node_name) + local lighting_node = wielded_light.get_lighting_node(node_name) + if lighting_node then + return lighting_node.level + end +end + +-- Check if a node name is one of the wielded light nodes +function wielded_light.get_lighting_node(node_name) + return lighting_nodes[node_name] +end + +-- Register any node as lightable, register all light level variations for it +function wielded_light.register_lightable_node(node_name, property_overrides, custom_prefix) + -- Node name must be string + if type(node_name) ~= "string" then + error_log("You must provide a node name to be registered as lightable, '%s' given.", type(node_name)) + return + end + + -- Node must already be registered + local original_definition = minetest.registered_nodes[node_name] + if not original_definition then + error_log("The node '%s' cannot be registered as lightable because it does not exist.", node_name) + return + end + + -- Decide the prefix for the lighting node + local prefix = custom_prefix or node_name:gsub(":", "_", 1, true) .. "_" + if lighting_prefixes[prefix] then + error_log("The lighting prefix '%s' cannot be used for '%s' as it is already used for '%s'.", prefix, node_name, lighting_prefixes[prefix]) + return + end + lighting_prefixes[prefix] = node_name + + -- Default for property overrides + if not property_overrides then property_overrides = {} end + + -- Copy the node definition and provide required settings for a lighting node + local new_definition = table.copy(original_definition) + new_definition.on_timer = cleanup_timer_callback + new_definition.paramtype = "light" + new_definition.mod_origin = mod_name + new_definition.groups = new_definition.groups or {} + new_definition.groups.not_in_creative_inventory = 1 + -- Make sure original node is dropped if a lit node is dug + if not new_definition.drop then + new_definition.drop = node_name + end + + -- Allow any properties to be overridden on registration + for prop, val in pairs(property_overrides) do + new_definition[prop] = val + end + + -- If it's a liquid, we need to stop it flowing + if new_definition.groups.liquid then + new_definition.liquid_range = 0 + lightable_liquids[node_name] = true + end + + -- Register the lighting nodes + lightable_nodes[node_name] = {} + for i=1, minetest.LIGHT_MAX do + local lighting_node_name = wielded_light.lighting_node_of_level(i, prefix) + + -- Index for quick finding later + lightable_nodes[node_name][i] = lighting_node_name + lighting_nodes[lighting_node_name] = { + node = node_name, + level = i + } + + -- Copy the base definition and apply the light level + local level_definition = table.copy(new_definition) + level_definition.light_source = i + + -- If it's a liquid, we need to stop it replacing itself with the original + if level_definition.groups.liquid then + level_definition.liquid_alternative_source = lighting_node_name + level_definition.liquid_alternative_flowing = lighting_node_name + end + + minetest.register_node(":"..lighting_node_name, level_definition) + end +end + +-- Check if node can have a wielded light node placed in it +function wielded_light.is_lightable_node(node_pos) + local name = minetest.get_node(node_pos).name + if lightable_nodes[name] then + return true + elseif wielded_light.get_lighting_node(name) then + return true + end + return false +end + +-- Gets the closest position to pos that's a lightable node +function wielded_light.get_light_position(pos) + local around_vector = { + {x=0, y=0, z=0}, + {x=0, y=1, z=0}, {x=0, y=-1, z=0}, + {x=1, y=0, z=0}, {x=-1, y=0, z=0}, + {x=0, y=0, z=1}, {x=0, y=0, z=-1}, + } + for _, around in ipairs(around_vector) do + local light_pos = vector.add(pos, around) + if wielded_light.is_lightable_node(light_pos) then + return light_pos + end + end +end + +-- Gets the emitted light level of a given item name +function wielded_light.get_light_def(item_name) + -- Invalid item? No light + if not item_name or item_name == "" then + return 0, false + end + + -- If the item is cached return the cached level + local cached_definition = shiny_items[item_name] + if cached_definition then + return cached_definition.level, cached_definition.floodable + end + + -- Get the item definition + local stack = ItemStack(item_name) + local itemdef = stack:get_definition() + + -- If invalid, no light + if not itemdef then + return 0, false + end + + -- Get the light level of an item from its definition + -- Reduce the light level by level_delta - original functionality + -- Limit between 0 and the max light level + return math.min(math.max((itemdef.light_source or 0) - level_delta, 0), minetest.LIGHT_MAX), itemdef.floodable +end + +-- Register an item as shining +function wielded_light.register_item_light(item_name, light_level, floodable) + if shiny_items[item_name] then + if light_level then + shiny_items[item_name].level = light_level + end + if floodable ~= nil then + shiny_items[item_name].floodable = floodable + end + else + if floodable == nil then + local stack = ItemStack(item_name) + local itemdef = stack:get_definition() + floodable = itemdef.floodable + end + shiny_items[item_name] = { + level = light_level, + floodable = floodable or false + } + end +end + +-- Mark an item as floodable or not +function wielded_light.register_item_floodable(item_name, floodable) + if floodable == nil then floodable = true end + if shiny_items[item_name] then + shiny_items[item_name].floodable = floodable + else + local calced_level = wielded_light.get_light_def(item_name) + shiny_items[item_name] = { + level = calced_level, + floodable = floodable + } + end +end + +-- Keep track of an item entity. Should be called once for an item +function wielded_light.track_item_entity(obj, cat, item) + if not is_entity_valid({ obj=obj }) then return end + + local light_level, light_is_floodable = wielded_light.get_light_def(item) + -- If the item does not emit light do not track it + if light_level <= 0 then return end + + -- Generate the uid for the item and the id for the light category + local uid = tostring(obj) + local id = get_light_category_id(cat)..uid + + -- Create the main tracking object for this item instance if it does not already exist + if not tracked_entities[uid] then + tracked_entities[uid] = { obj=obj, items={}, update = true } + end + + -- Create the item tracking object for this item + category + tracked_entities[uid].items[id] = { level=light_level, floodable=light_is_floodable } + + -- Add the light in on creation so it's immediate + local pos = entity_pos(obj) + local pos_str = pos and minetest.pos_to_string(pos) + if pos_str then + if not (light_is_floodable and is_lightable_liquid(pos)) then + add_light(pos_str, id, light_level) + end + end + tracked_entities[uid].pos = pos_str +end + +-- A player's light should appear near their head not their feet +local player_height_offset = { x=0, y=1, z=0 } + +-- Keep track of a user / player entity. Should be called as often as the user updates +function wielded_light.track_user_entity(obj, cat, item) + -- Generate the uid for the player and the id for the light category + local uid = tostring(obj) + local id = get_light_category_id(cat)..uid + + -- Create the main tracking object for this player instance if it does not already exist + if not tracked_entities[uid] then + tracked_entities[uid] = { obj=obj, items={}, offset = player_height_offset, update = true } + end + + local tracked_entity = tracked_entities[uid] + local tracked_item = tracked_entity.items[id] + + -- If the item being tracked for the player changes, update the item tracking object for this item + category + if not tracked_item or tracked_item.item ~= item then + local light_level, light_is_floodable = wielded_light.get_light_def(item) + tracked_entity.items[id] = { level=light_level, item=item, floodable=light_is_floodable } + tracked_entity.update = true + end +end + + +-- Setup -- + +-- Wielded item shining globalstep +minetest.register_globalstep(global_timer_callback) + +-- Dropped item on_step override +-- https://github.com/minetest/minetest/issues/6909 +local builtin_item = minetest.registered_entities["__builtin:item"] +local item = { + on_step = function(self, dtime, ...) + builtin_item.on_step(self, dtime, ...) + -- Register an item once for tracking + -- If it's already being tracked, exit + if self.wielded_light then return end + self.wielded_light = true + local stack = ItemStack(self.itemstring) + local item_name = stack:get_name() + wielded_light.track_item_entity(self.object, "item", item_name) + end +} +setmetatable(item, {__index = builtin_item}) +minetest.register_entity(":__builtin:item", item) + +-- Track a player's wielded item +wielded_light.register_player_lightstep(function (player) + wielded_light.track_user_entity(player, "wield", player:get_wielded_item():get_name()) +end) + +-- Register helper nodes +wielded_light.register_lightable_node("air", nil, "") + +if minetest.get_modpath("default") then + wielded_light.register_lightable_node("default:water_source", nil, "water_") + wielded_light.register_lightable_node("default:river_water_source", nil, "river_water_") +elseif minetest.get_modpath("hades_core") then + wielded_light.register_lightable_node("hades_core:water_source", nil, "water_") +else + error_log("Not running in a supported game, lightable water disabled") +end + +---TEST +--wielded_light.register_item_light('default:dirt', 14) diff --git a/mods/misc/wielded_light/mod.conf b/mods/misc/wielded_light/mod.conf new file mode 100644 index 0000000..855b69d --- /dev/null +++ b/mods/misc/wielded_light/mod.conf @@ -0,0 +1,2 @@ +name = wielded_light +optional_depends = default, hades_core, builtin_item diff --git a/mods/misc/wielded_light/screenshot.png b/mods/misc/wielded_light/screenshot.png new file mode 100644 index 0000000..11f4404 Binary files /dev/null and b/mods/misc/wielded_light/screenshot.png differ diff --git a/mods/mtg/xpanes/README.txt b/mods/mtg/xpanes/README.txt new file mode 100644 index 0000000..7e2a1be --- /dev/null +++ b/mods/mtg/xpanes/README.txt @@ -0,0 +1,32 @@ +Minetest Game mod: xpanes +========================= +See license.txt for license information. + +Authors of source code +---------------------- +Originally by xyz (MIT) +BlockMen (MIT) +sofar (MIT) +Various Minetest Game developers and contributors (MIT) + +Authors of media (textures) +--------------------------- +xyz (CC BY-SA 3.0): + All textures not mentioned below. + +Gambit (CC BY-SA 3.0): + xpanes_bar.png + +paramat (CC BY-SA 3.0): + xpanes_bar_top.png + +Krock (CC0 1.0): + xpanes_edge.png + +TumeniNodes (CC BY-SA 3.0): + xpanes_door_steel_bar.png + xpanes_item_steel_bar.png + xpanes_trapdoor_steel_bar.png + xpanes_trapdoor_steel_bar_side.png + xpanes_steel_bar_door_close.ogg + xpanes_steel_bar_door_open.ogg diff --git a/mods/mtg/xpanes/init.lua b/mods/mtg/xpanes/init.lua new file mode 100644 index 0000000..a02dbfe --- /dev/null +++ b/mods/mtg/xpanes/init.lua @@ -0,0 +1,261 @@ +-- xpanes/init.lua + +-- Load support for MT game translation. +local S = minetest.get_translator("xpanes") + + +local function is_pane(pos) + return minetest.get_item_group(minetest.get_node(pos).name, "pane") > 0 +end + +local function connects_dir(pos, name, dir) + local aside = vector.add(pos, minetest.facedir_to_dir(dir)) + if is_pane(aside) then + return true + end + + local connects_to = minetest.registered_nodes[name].connects_to + if not connects_to then + return false + end + local list = minetest.find_nodes_in_area(aside, aside, connects_to) + + if #list > 0 then + return true + end + + return false +end + +local function swap(pos, node, name, param2) + if node.name == name and node.param2 == param2 then + return + end + + minetest.swap_node(pos, {name = name, param2 = param2}) +end + +local function update_pane(pos) + if not is_pane(pos) then + return + end + local node = minetest.get_node(pos) + local name = node.name + if name:sub(-5) == "_flat" then + name = name:sub(1, -6) + end + + local any = node.param2 + local c = {} + local count = 0 + for dir = 0, 3 do + c[dir] = connects_dir(pos, name, dir) + if c[dir] then + any = dir + count = count + 1 + end + end + + if count == 0 then + swap(pos, node, name .. "_flat", any) + elseif count == 1 then + swap(pos, node, name .. "_flat", (any + 1) % 4) + elseif count == 2 then + if (c[0] and c[2]) or (c[1] and c[3]) then + swap(pos, node, name .. "_flat", (any + 1) % 4) + else + swap(pos, node, name, 0) + end + else + swap(pos, node, name, 0) + end +end + +minetest.register_on_placenode(function(pos, node) + if minetest.get_item_group(node, "pane") then + update_pane(pos) + end + for i = 0, 3 do + local dir = minetest.facedir_to_dir(i) + update_pane(vector.add(pos, dir)) + end +end) + +minetest.register_on_dignode(function(pos) + for i = 0, 3 do + local dir = minetest.facedir_to_dir(i) + update_pane(vector.add(pos, dir)) + end +end) + +xpanes = {} +function xpanes.register_pane(name, def) + for i = 1, 15 do + minetest.register_alias("xpanes:" .. name .. "_" .. i, "xpanes:" .. name .. "_flat") + end + + local flatgroups = table.copy(def.groups) + flatgroups.pane = 1 + minetest.register_node(":xpanes:" .. name .. "_flat", { + description = def.description, + drawtype = "nodebox", + paramtype = "light", + is_ground_content = false, + sunlight_propagates = true, + inventory_image = def.inventory_image, + wield_image = def.wield_image, + paramtype2 = "facedir", + tiles = { + def.textures[3], + def.textures[3], + def.textures[3], + def.textures[3], + def.textures[1], + def.textures[1] + }, + groups = flatgroups, + drop = "xpanes:" .. name .. "_flat", + sounds = def.sounds, + use_texture_alpha = def.use_texture_alpha and "blend" or "clip", + node_box = { + type = "fixed", + fixed = {{-1/2, -1/2, -1/32, 1/2, 1/2, 1/32}}, + }, + selection_box = { + type = "fixed", + fixed = {{-1/2, -1/2, -1/32, 1/2, 1/2, 1/32}}, + }, + connect_sides = { "left", "right" }, + }) + + local groups = table.copy(def.groups) + groups.pane = 1 + groups.not_in_creative_inventory = 1 + minetest.register_node(":xpanes:" .. name, { + drawtype = "nodebox", + paramtype = "light", + is_ground_content = false, + sunlight_propagates = true, + description = def.description, + tiles = { + def.textures[3], + def.textures[3], + def.textures[1] + }, + groups = groups, + drop = "xpanes:" .. name .. "_flat", + sounds = def.sounds, + use_texture_alpha = def.use_texture_alpha and "blend" or "clip", + node_box = { + type = "connected", + fixed = {{-1/32, -1/2, -1/32, 1/32, 1/2, 1/32}}, + connect_front = {{-1/32, -1/2, -1/2, 1/32, 1/2, -1/32}}, + connect_left = {{-1/2, -1/2, -1/32, -1/32, 1/2, 1/32}}, + connect_back = {{-1/32, -1/2, 1/32, 1/32, 1/2, 1/2}}, + connect_right = {{1/32, -1/2, -1/32, 1/2, 1/2, 1/32}}, + }, + connects_to = {"group:pane", "group:stone", "group:glass", "group:wood", "group:tree"}, + }) + + minetest.register_craft({ + output = "xpanes:" .. name .. "_flat 16", + recipe = def.recipe + }) +end + +xpanes.register_pane("pane", { + description = S("Glass Pane"), + textures = {"default_glass.png", "", "xpanes_edge.png"}, + inventory_image = "default_glass.png", + wield_image = "default_glass.png", + sounds = default.node_sound_glass_defaults(), + groups = {snappy=2, cracky=3, oddly_breakable_by_hand=3}, + recipe = { + {"default:glass", "default:glass", "default:glass"}, + {"default:glass", "default:glass", "default:glass"} + } +}) + +xpanes.register_pane("obsidian_pane", { + description = S("Obsidian Glass Pane"), + textures = {"default_obsidian_glass.png", "", "xpanes_edge_obsidian.png"}, + inventory_image = "default_obsidian_glass.png", + wield_image = "default_obsidian_glass.png", + sounds = default.node_sound_glass_defaults(), + groups = {snappy=2, cracky=3}, + recipe = { + {"default:obsidian_glass", "default:obsidian_glass", "default:obsidian_glass"}, + {"default:obsidian_glass", "default:obsidian_glass", "default:obsidian_glass"} + } +}) + +xpanes.register_pane("bar", { + description = S("Steel Bars"), + textures = {"xpanes_bar.png", "", "xpanes_bar_top.png"}, + inventory_image = "xpanes_bar.png", + wield_image = "xpanes_bar.png", + groups = {cracky=2}, + sounds = default.node_sound_metal_defaults(), + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"} + } +}) + +minetest.register_lbm({ + name = "xpanes:gen2", + nodenames = {"group:pane"}, + action = function(pos, node) + update_pane(pos) + for i = 0, 3 do + local dir = minetest.facedir_to_dir(i) + update_pane(vector.add(pos, dir)) + end + end +}) + +-- Register steel bar doors and trapdoors + +if minetest.get_modpath("doors") then + + doors.register("xpanes:door_steel_bar", { + tiles = {{name = "xpanes_door_steel_bar.png", backface_culling = true}}, + description = S("Steel Bar Door"), + inventory_image = "xpanes_item_steel_bar.png", + protected = true, + groups = {node = 1, cracky = 1, level = 2}, + sounds = default.node_sound_metal_defaults(), + sound_open = "xpanes_steel_bar_door_open", + sound_close = "xpanes_steel_bar_door_close", + gain_open = 0.15, + gain_close = 0.13, + recipe = { + {"xpanes:bar_flat", "xpanes:bar_flat"}, + {"xpanes:bar_flat", "xpanes:bar_flat"}, + {"xpanes:bar_flat", "xpanes:bar_flat"}, + }, + }) + + doors.register_trapdoor("xpanes:trapdoor_steel_bar", { + description = S("Steel Bar Trapdoor"), + inventory_image = "xpanes_trapdoor_steel_bar.png", + wield_image = "xpanes_trapdoor_steel_bar.png", + tile_front = "xpanes_trapdoor_steel_bar.png", + tile_side = "xpanes_trapdoor_steel_bar_side.png", + protected = true, + groups = {node = 1, cracky = 1, level = 2, door = 1}, + sounds = default.node_sound_metal_defaults(), + sound_open = "xpanes_steel_bar_door_open", + sound_close = "xpanes_steel_bar_door_close", + gain_open = 0.15, + gain_close = 0.13, + }) + + minetest.register_craft({ + output = "xpanes:trapdoor_steel_bar", + recipe = { + {"xpanes:bar_flat", "xpanes:bar_flat"}, + {"xpanes:bar_flat", "xpanes:bar_flat"}, + } + }) +end diff --git a/mods/mtg/xpanes/license.txt b/mods/mtg/xpanes/license.txt new file mode 100644 index 0000000..c1f31e3 --- /dev/null +++ b/mods/mtg/xpanes/license.txt @@ -0,0 +1,65 @@ +License of source code +---------------------- + +The MIT License (MIT) +Copyright (C) 2014-2016 xyz +Copyright (C) 2014-2016 BlockMen +Copyright (C) 2016 Auke Kok +Copyright (C) 2014-2016 Various Minetest Game developers and contributors + +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. + +For more details: +https://opensource.org/licenses/MIT + + +Licenses of media (textures) +---------------------------- + +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) +Copyright (C) 2014-2016 xyz +Copyright (C) 2013-2016 Gambit +Copyright (C) 2016 paramat +Copyright (C) 2019 TumeniNodes + +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/mtg/xpanes/locale/template.txt b/mods/mtg/xpanes/locale/template.txt new file mode 100644 index 0000000..08dfbba --- /dev/null +++ b/mods/mtg/xpanes/locale/template.txt @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane= +Obsidian Glass Pane= +Steel Bars= +Steel Bar Door= +Steel Bar Trapdoor= diff --git a/mods/mtg/xpanes/locale/xpanes.bg.tr b/mods/mtg/xpanes/locale/xpanes.bg.tr new file mode 100644 index 0000000..e11e834 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.bg.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Стъклен прозорец +Obsidian Glass Pane=Прозорец от обсидианово стъкло +Steel Bars=Стоманени решетки +Steel Bar Door=Стоманени решетки за врата +Steel Bar Trapdoor=Стоманени решетки за капак diff --git a/mods/mtg/xpanes/locale/xpanes.de.tr b/mods/mtg/xpanes/locale/xpanes.de.tr new file mode 100644 index 0000000..9852753 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.de.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Glasscheibe +Obsidian Glass Pane=Obsidianglasscheibe +Steel Bars=Stahlgitter +Steel Bar Door=Stahlgittertür +Steel Bar Trapdoor=Stahlgitterfalltür diff --git a/mods/mtg/xpanes/locale/xpanes.eo.tr b/mods/mtg/xpanes/locale/xpanes.eo.tr new file mode 100644 index 0000000..cfbbfb5 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.eo.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Vitra vitraĵo +Obsidian Glass Pane=Obsidiana vitra vitraĵo +Steel Bars=Ŝtalaj baraĵoj +Steel Bar Door=Ŝtala baraĵa pordo +Steel Bar Trapdoor=Ŝtala baraĵa plankpordo diff --git a/mods/mtg/xpanes/locale/xpanes.es.tr b/mods/mtg/xpanes/locale/xpanes.es.tr new file mode 100644 index 0000000..9902be7 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.es.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Panel de vidrio +Obsidian Glass Pane=Panel de vidrio de obsidiana +Steel Bars=Barras de acero +Steel Bar Door=Puerta de barras de acero +Steel Bar Trapdoor=Trampilla de barras de acero diff --git a/mods/mtg/xpanes/locale/xpanes.eu.tr b/mods/mtg/xpanes/locale/xpanes.eu.tr new file mode 100644 index 0000000..c41c448 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.eu.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Beirazko panela +Obsidian Glass Pane=Obsidiana-beirazko panela +Steel Bars=Altzairuzko barrak +Steel Bar Door=Altzairu-barrazko atea +Steel Bar Trapdoor=Altzairu-barrazko tranpola diff --git a/mods/mtg/xpanes/locale/xpanes.fr.tr b/mods/mtg/xpanes/locale/xpanes.fr.tr new file mode 100644 index 0000000..c751799 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.fr.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Panneau de verre +Obsidian Glass Pane=Panneau de verre d'obsidienne +Steel Bars=Barreaux d'acier +Steel Bar Door=Porte en barreaux d'acier +Steel Bar Trapdoor=Trappe en barreaux d'acier diff --git a/mods/mtg/xpanes/locale/xpanes.id.tr b/mods/mtg/xpanes/locale/xpanes.id.tr new file mode 100644 index 0000000..906cc0f --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.id.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Panel Kaca +Obsidian Glass Pane=Panel Kaca Obsidian +Steel Bars=Batang Baja +Steel Bar Door=Pintu Batang Baja +Steel Bar Trapdoor=Pintu Kolong Batang Baja diff --git a/mods/mtg/xpanes/locale/xpanes.it.tr b/mods/mtg/xpanes/locale/xpanes.it.tr new file mode 100644 index 0000000..63c8b62 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.it.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Pannello di vetro +Obsidian Glass Pane=Pannello di vetro d'ossidiana +Steel Bars=Sbarre d'acciaio +Steel Bar Door=Porta con sbarre d'acciaio +Steel Bar Trapdoor=Botola con sbarre d'acciaio diff --git a/mods/mtg/xpanes/locale/xpanes.ja.tr b/mods/mtg/xpanes/locale/xpanes.ja.tr new file mode 100644 index 0000000..06e0bed --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.ja.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=板ガラス +Obsidian Glass Pane=黒曜石の板ガラス +Steel Bars=鉄棒の柵 +Steel Bar Door=鉄棒のドア +Steel Bar Trapdoor=鉄棒のトラップドア diff --git a/mods/mtg/xpanes/locale/xpanes.jbo.tr b/mods/mtg/xpanes/locale/xpanes.jbo.tr new file mode 100644 index 0000000..333b531 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.jbo.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=lo blaci plita +Obsidian Glass Pane=lo je'erma'ablaci blaci plita +Steel Bars=lo gasta garna +Steel Bar Door=lo gasta garna vrogai +Steel Bar Trapdoor=lo gasta garna lolvrogai diff --git a/mods/mtg/xpanes/locale/xpanes.lv.tr b/mods/mtg/xpanes/locale/xpanes.lv.tr new file mode 100644 index 0000000..c75849c --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.lv.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Stikla panelis +Obsidian Glass Pane=Obsidiāna stikla panelis +Steel Bars=Tērauda režģis +Steel Bar Door=Tērauda režģa durvis +Steel Bar Trapdoor=Tērauda režģa lūka diff --git a/mods/mtg/xpanes/locale/xpanes.ms.tr b/mods/mtg/xpanes/locale/xpanes.ms.tr new file mode 100644 index 0000000..dedfefa --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.ms.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Kaca Tingkap +Obsidian Glass Pane=Kaca Tingkap Obsidia +Steel Bars=Jeriji Keluli +Steel Bar Door=Pintu Jeriji Keluli +Steel Bar Trapdoor=Pintu Kolong Jeriji Keluli diff --git a/mods/mtg/xpanes/locale/xpanes.pl.tr b/mods/mtg/xpanes/locale/xpanes.pl.tr new file mode 100644 index 0000000..0ebb386 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.pl.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Szyba +Obsidian Glass Pane=Obsydianowa szyba +Steel Bars=Stalowe kraty +Steel Bar Door=Drzwi ze stalowych krat +Steel Bar Trapdoor=Właz ze stalowych krat diff --git a/mods/mtg/xpanes/locale/xpanes.pt_BR.tr b/mods/mtg/xpanes/locale/xpanes.pt_BR.tr new file mode 100644 index 0000000..c1ca3b2 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.pt_BR.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Painel de Vidro +Obsidian Glass Pane=Painel de Vidro de Obsidiana +Steel Bars=Barras de Aço +Steel Bar Door=Porta de Barras de Aço +Steel Bar Trapdoor=Alçapão de Barras de Aço diff --git a/mods/mtg/xpanes/locale/xpanes.ru.tr b/mods/mtg/xpanes/locale/xpanes.ru.tr new file mode 100644 index 0000000..cd7173e --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.ru.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Стеклянная панель +Obsidian Glass Pane=Панель из обсидианового стекла +Steel Bars=Стальная решетка +Steel Bar Door=Стальная решётчатая дверь +Steel Bar Trapdoor=Стальной решётчатый люк diff --git a/mods/mtg/xpanes/locale/xpanes.sk.tr b/mods/mtg/xpanes/locale/xpanes.sk.tr new file mode 100644 index 0000000..0d07e08 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.sk.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Tabuľa skla +Obsidian Glass Pane=Tabuľa obsidiánového skla +Steel Bars=Oceľové mreže +Steel Bar Door=Dvere z oceľových mreží +Steel Bar Trapdoor=Padajúce dvere z oceľových mreží diff --git a/mods/mtg/xpanes/locale/xpanes.sv.tr b/mods/mtg/xpanes/locale/xpanes.sv.tr new file mode 100644 index 0000000..7b615dd --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.sv.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Glasruta +Obsidian Glass Pane=Obsidianglasruta +Steel Bars=Stålgaller +Steel Bar Door=Stålgallerdörr +Steel Bar Trapdoor=Stålgallerfallucka diff --git a/mods/mtg/xpanes/locale/xpanes.uk.tr b/mods/mtg/xpanes/locale/xpanes.uk.tr new file mode 100644 index 0000000..41033bd --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.uk.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=Скляна панель +Obsidian Glass Pane=Панель з обсидіанового скла +Steel Bars=Ґрати +Steel Bar Door=Двері з ґратами +Steel Bar Trapdoor=Люк з ґратами diff --git a/mods/mtg/xpanes/locale/xpanes.zh_CN.tr b/mods/mtg/xpanes/locale/xpanes.zh_CN.tr new file mode 100644 index 0000000..7b1871c --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.zh_CN.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=玻璃窗 +Obsidian Glass Pane=黑曜石玻璃窗 +Steel Bars=钢筋 +Steel Bar Door=钢筋门 +Steel Bar Trapdoor=钢筋活板门 diff --git a/mods/mtg/xpanes/locale/xpanes.zh_TW.tr b/mods/mtg/xpanes/locale/xpanes.zh_TW.tr new file mode 100644 index 0000000..97ee7a2 --- /dev/null +++ b/mods/mtg/xpanes/locale/xpanes.zh_TW.tr @@ -0,0 +1,6 @@ +# textdomain: xpanes +Glass Pane=玻璃窗 +Obsidian Glass Pane=黑曜石玻璃窗 +Steel Bars=鋼筋 +Steel Bar Door=鋼筋門 +Steel Bar Trapdoor=鋼筋活板門 diff --git a/mods/mtg/xpanes/mod.conf b/mods/mtg/xpanes/mod.conf new file mode 100644 index 0000000..dcb0716 --- /dev/null +++ b/mods/mtg/xpanes/mod.conf @@ -0,0 +1,4 @@ +name = xpanes +description = Minetest Game mod: xpanes +depends = default +optional_depends = doors diff --git a/mods/mtg/xpanes/sounds/xpanes_steel_bar_door_close.ogg b/mods/mtg/xpanes/sounds/xpanes_steel_bar_door_close.ogg new file mode 100644 index 0000000..0620bfb Binary files /dev/null and b/mods/mtg/xpanes/sounds/xpanes_steel_bar_door_close.ogg differ diff --git a/mods/mtg/xpanes/sounds/xpanes_steel_bar_door_open.ogg b/mods/mtg/xpanes/sounds/xpanes_steel_bar_door_open.ogg new file mode 100644 index 0000000..d159be9 Binary files /dev/null and b/mods/mtg/xpanes/sounds/xpanes_steel_bar_door_open.ogg differ diff --git a/mods/mtg/xpanes/textures/xpanes_bar.png b/mods/mtg/xpanes/textures/xpanes_bar.png new file mode 100644 index 0000000..3ea62a9 Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_bar.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_bar_top.png b/mods/mtg/xpanes/textures/xpanes_bar_top.png new file mode 100644 index 0000000..2955d72 Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_bar_top.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_door_steel_bar.png b/mods/mtg/xpanes/textures/xpanes_door_steel_bar.png new file mode 100644 index 0000000..39f45c3 Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_door_steel_bar.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_edge.png b/mods/mtg/xpanes/textures/xpanes_edge.png new file mode 100644 index 0000000..5768d66 Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_edge.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_edge_obsidian.png b/mods/mtg/xpanes/textures/xpanes_edge_obsidian.png new file mode 100644 index 0000000..abdd14e Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_edge_obsidian.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_item_steel_bar.png b/mods/mtg/xpanes/textures/xpanes_item_steel_bar.png new file mode 100644 index 0000000..46e4d9c Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_item_steel_bar.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_trapdoor_steel_bar.png b/mods/mtg/xpanes/textures/xpanes_trapdoor_steel_bar.png new file mode 100644 index 0000000..a56c5ee Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_trapdoor_steel_bar.png differ diff --git a/mods/mtg/xpanes/textures/xpanes_trapdoor_steel_bar_side.png b/mods/mtg/xpanes/textures/xpanes_trapdoor_steel_bar_side.png new file mode 100644 index 0000000..a71231e Binary files /dev/null and b/mods/mtg/xpanes/textures/xpanes_trapdoor_steel_bar_side.png differ diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..6d5a1a8 Binary files /dev/null and b/screenshot.png differ