diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 8549f66..fb3678b --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ -# runes +Rune mod by Mg +############## -Dépôt du mod runes. Extrait du dépôt MFF Classic. \ No newline at end of file +# Purpose +This mod proposes 3 runes used in the balance of solarmana. + +# Authors + - Mg (@LeMagnesium) + +# Medias + diff --git a/amulets.lua b/amulets.lua new file mode 100755 index 0000000..7e2f2c9 --- /dev/null +++ b/amulets.lua @@ -0,0 +1,94 @@ +-- Amulets +runes.functions.register_amulet("silver_cross", "Silver Cross", 1, 25 ) +runes.functions.register_amulet("minotaur_eye", "Minotaur Eye", 1, 50 ) +runes.functions.register_amulet("hellfire", "Hellfire Amulet", 1, 75 ) +runes.functions.register_amulet("grim_reaper", "Grim Reaper's Amulet", 1, 100) + +-- Recipes +minetest.register_craft({ + output = "runes:silver_cross_amulet", + recipe = { + {"", "farming:cotton", ""}, + {"default:silver_ingot", "default:silver_ingot", "default:silver_ingot"}, + {"", "default:silver_ingot", ""}, + } +}) + +minetest.register_craft({ + output = "runes:minotaur_eye_amulet", + recipe = { + {"", "darkage:chain", ""}, + {"technic:brass_ingot", "mobs:minotaur_eye", "technic:brass_ingot"}, + {"", "mesecons_materials:glue", ""}, + } +}) + +minetest.register_craft({ + output = "runes:hellfire_amulet", + recipe = { + {"", "darkage:chain", ""}, + {"mobs:lava_orb", "default:diamondblock", "mobs:lava_orb"}, + {"default:obsidian", "bucket:bucket_lava", "default:obsidian"}, + } +}) + +minetest.register_craft({ + output = "runes:grim_reaper_amulet", + recipe = { + {"", "runes:hellfire_amulet", ""}, + {"nether:white", "runes:black_magic_block", "nether:white"}, + {"", "default:mithrilblock", ""}, + } +}) + +minetest.register_craft({ + output = "runes:black_magic_block", + recipe = { + {"nether:white", "runes:hellfire_amulet", "default:obsidian"}, + {"default:nyancat", "nether:tree", "default:mese"}, + {"default:obsidian", "default:mithrilblock", "nether:white"}, + } +}) + +-- Nodes +minetest.register_node("runes:black_magic_block", { + description = "Black Magic Block", + tiles = {"runes_black_magic_block.png"}, + is_ground_content = true, + paramtype2 = "facedir", + groups = {crumbly=3}, + sounds = default.node_sound_sand_defaults(), +}) + +-- Globalstep checking for the amulets +tmpdata = {} +loop = function() + for _, player in pairs(minetest.get_connected_players()) do + local inv = player:get_inventory() + local pname = player:get_player_name() + local basemana = mana.getmax(pname) - (tmpdata[pname] or 0) -- Baseline mana, without any amulets + local addons = 0 + for index, item in pairs(inv:get_list("main")) do + local itemname = item:get_name() + local itemcount = item:get_count() + for name, manadiff in pairs(runes.datas.amulets) do + if itemname == "runes:" .. name .. "_amulet" then + addons = addons + (manadiff * itemcount) + --print("Detected " .. name) + end + end + end + mana.setmax(pname, basemana + addons) + tmpdata[pname] = addons + end + minetest.after(1, loop) +end + +minetest.after(0, loop) + +minetest.register_on_leaveplayer(function(player) + local pname = player:get_player_name() + mana.setmax(pname, mana.getmax(pname) - tmpdata[pname]) -- Reset + tmpdata[pname] = nil + mana.save_to_file(pname) -- Double class since we aren't sure mana hasn't already saved (it probably did) +end) diff --git a/api.lua b/api.lua new file mode 100755 index 0000000..10de6ff --- /dev/null +++ b/api.lua @@ -0,0 +1,301 @@ +-- API of the rune mod -- + +-- Global namespace +runes = {} +runes.datas = {} +runes.datas.handlers = {} +runes.datas.items = {} +runes.datas.amulets = {} +runes.functions = {} + +-- Simple rune register function +runes.functions.register_rune = function(parameters) + local runedef = {} + + if not parameters.name then + minetest.log("ERROR","[runes] Cannot register rune : no name") + return + end + + runedef.name = parameters.name + runedef.desc = parameters.description or "" + runedef.img = parameters.img or "default_stone.png" + if type(runedef.img) ~= "table" then + runedef.img = { + ["minor"] = runedef.img .. "_minor.png", + ["medium"] = runedef.img .. "_medium.png", + ["major"] = runedef.img .. "_major.png", + } + end + runedef.type = parameters.type or "craftitem" + runedef.needed_mana = parameters.needed_mana or {["minor"] = 0, ["medium"] = 5, ["major"] = 10} + + runes.datas.handlers[runedef.name] = {} + + if runedef.type == "cube" then + for _, level in pairs({"minor", "medium", "major"}) do + minetest.register_node("runes:rune_" .. runedef.name .. "_" .. level, { + description = runedef.desc .. " ("..level..")", + tiles = {runedef.img[level]}, + groups = {oddly_breakable_by_hand = 2, rune = 1}, + after_place_node = function(pos, placer, itemstack, pointed_thing) + if placer and placer:is_player() then + local meta = minetest.get_meta(pos) + meta:set_string("owner",placer:get_player_name()) + end + if runes.datas.handlers[runedef.name].on_place then + if mana.get(placer:get_player_name()) >= runedef.needed_mana[level] then + local answer = runes.datas.handlers[runedef.name].on_place(level, pos, placer, itemstack, pointed_thing) + -- True answer leads to no dicrease (generally because the handler did it itself) + if not answer then + mana.subtract(placer:get_player_name(),runedef.needed_mana[level]) + end + else + minetest.chat_send_player(placer:get_player_name(),"Not enough mana (needed : " .. runedef.needed_mana[level] ..")") + end + end + end, + can_dig = function(pos, player) + if runes.datas.handlers[runedef.name].can_dig then + return runes.datas.handlers[runedef.name].can_dig(level, pos, player) + else + return true + end + end, + on_punch = function(pos, node, puncher, pointed_thing) + if runes.datas.handlers[runedef.name].on_punch then + if mana.get(puncher:get_player_name()) >= runedef.needed_mana[level] then + runes.datas.handlers[runedef.name].on_punch(level, pos, node, puncher, pointed_thing) + mana.subtract(puncher:get_player_name(),runedef.needed_mana[level]) + else + minetest.chat_send_player(puncher:get_player_name(),"Not enough mana (needed : " .. runedef.needed_mana[level] ..")") + end + end + end, + --[[after_dig_node = function(pos, oldnode, oldmetadata, digger) + --if runes.datas.handlers[runedef.name].can_dig and runes.datas.handlers[runedef.name].can_dig(pos, digger) then + if runes.datas.handlers[runedef.name].on_dig then + if mana.get(digger:get_player_name()) >= runedef.needed_mana[level] then + runes.datas.handlers[runedef.name].on_dig(pos, oldnode, digger) + mana.subtract(digger:get_player_name(),runedef.needed_mana) + else + minetest.chat_send_player(digger:get_player_name(),"Not enough mana (needed : " .. runedef.needed_mana[level] ..")") + end + end + end + end,]] + }) + end + + elseif runedef.type == "plate" then + for _, level in pairs({"minor", "medium", "major"}) do + minetest.register_node("runes:rune_" .. runedef.name .. "_" .. level, { + description = runedef.desc .. " (" .. level .. ")", + paramtype = "light", + inventory_image = runedef.img[level], + sunlight_propagates = true, + walkable = false, + tiles = {runedef.img[level]}, + groups = {rune = 1, oddly_breakable_by_hand = 2}, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, -0.499, 0.5}, + } + }, + after_place_node = function(pos, placer, itemstack, pointed_thing) + if placer and placer:is_player() then + local meta = minetest.get_meta(pos) + meta:set_string("owner",placer:get_player_name()) + end + if runes.datas.handlers[runedef.name].on_place then + if mana.get(placer:get_player_name()) >= runedef.needed_mana[level] then + local answer = runes.datas.handlers[runedef.name].on_place(level, pos, placer, itemstack, pointed_thing) + if not answer then + mana.subtract(placer:get_player_name(),runedef.needed_mana[level]) + end + else + minetest.chat_send_player(placer:get_player_name(),"Not enough mana (needed : " .. runedef.needed_mana[level] ..")") + end + end + end, + can_dig = function(pos, player) + if runes.datas.handlers[runedef.name].can_dig then + return runes.datas.handlers[runedef.name].can_dig(level, pos, player) + else + return true + end + end, + on_punch = function(pos, node, puncher, pointed_thing) + if runes.datas.handlers[runedef.name].on_punch then + if mana.get(placer:get_player_name()) >= runedef.needed_mana[level] then + local answer = runes.datas.handlers[runedef.name].on_punch(level, pos, node, puncher, pointed_thing) + if not answer then + mana.subtract(placer:get_player_name(),runedef.needed_mana[level]) + end + else + minetest.chat_send_player(placer:get_player_name(),"Not enough mana (needed : " .. runedef.needed_mana[level] ..")") + end + end + end, + }) + end + + elseif runedef.type == "craftitem" then + for _, level in pairs({"minor", "medium", "major"}) do + minetest.register_craftitem("runes:rune_" .. runedef.name .. "_" .. level, { + description = runedef.desc .. " (" .. level ..")", + inventory_image = runedef.img[level], + groups = {rune = 1}, + on_use = function(itemstack, user, pointed_thing) + -- Let the handler do its job + if runes.datas.handlers[runedef.name].on_use then + if mana.get(user:get_player_name()) >= runedef.needed_mana[level] then + local answer = runes.datas.handlers[runedef.name].on_use(level, itemstack, user, pointed_thing) + if not answer then + mana.subtract(user:get_player_name(),runedef.needed_mana[level]) + user:get_inventory():remove_item("main",{name = runedef.name .. "_" .. level}) + end + else + minetest.chat_send_player(user:get_player_name(),"Not enough mana (needed : " .. runedef.needed_mana[level] ..")") + end + end + end + }) + end + end +end + + +-- Handler connector +runes.functions.connect = function(itemname, callback, handler) + --[[ Available callback : + use + place + dig + can_dig + punch + ]] + + if not runes.datas.items[itemname] then + minetest.log("error","[runes] Cannot connect handler at " .. handler .. " to unknown item " .. itemname) + return + end + + if callback == "use" then + runes.datas.handlers[itemname].on_use = handler + elseif callback == "place" then + runes.datas.handlers[itemname].on_place = handler + elseif callback == "dig" then + runes.datas.handlers[itemname].on_dig = handler + elseif callback == "can_dig" then + runes.datas.handlers[itemname].can_dig = handler + elseif callback == "punch" then + runes.datas.handlers[itemname].on_punch = handler + else + minetest.log("error","[runes] Cannot connect handler to item's " .. itemname .. " unknown " .. callback .. " callback") + return + end +end + +-- Amulets + +runes.functions.register_amulet = function(name, desc, maxcount, manadiff) + minetest.register_craftitem("runes:" .. name .. "_amulet", { + description = desc, + inventory_image = "runes_" .. name .. "_amulet.png", + groups = {amulet = 1, rune = 1}, + stack_max = maxcount, + }) + runes.datas.amulets[name] = manadiff +end + +-- Inventory + +unified_inventory.register_button("runes", { + type = "image", + image = "runes_heal_major.png", + tooltip = "Rune inventory", + show_with = false, --Modif MFF (Crabman 30/06/2015) +}) +unified_inventory.register_page("runes", { + get_formspec = function(player) + local name = player:get_player_name() + local formspec = "background[0,0.2;8,4;runes_rune_inventory.png]".. + "background[0,4.5;8,4;ui_main_inventory.png]".. + "size[8,10]".. + "list[detached:"..name.."_runes;runes;0,0.2;8,4;]" + return {formspec=formspec} + end, +}) + +runes.functions.register_detached_inventory = function(player) + local name = player:get_player_name() + local player_inv = player:get_inventory() + local datafile = io.open(minetest.get_worldpath().."/runes/"..name.."_rune.inv", "r") + local rune_inv_data = {} + if datafile then + local line = datafile:read() + if line then + rune_inv_data = minetest.deserialize(line) + end + io.close(datafile) + end + local rune_inv = minetest.create_detached_inventory(name.."_runes",{ + on_put = function(inv, listname, index, stack, player) + player:get_inventory():set_stack(listname, index, stack) + armor:set_player_armor(player) + armor:update_inventory(player) + end, + on_take = function(inv, listname, index, stack, player) + player:get_inventory():set_stack(listname, index, nil) + armor:set_player_armor(player) + armor:update_inventory(player) + end, + allow_put = function(inv, listname, index, stack, player) + if minetest.get_item_group(stack:get_name(), "rune") > 0 then + return stack:get_count() + end + return 0 + end, + allow_take = function(inv, listname, index, stack, player) + return stack:get_count() + end, + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return count + end, + }) + rune_inv:set_size("runes", 8*4) + rune_inv:set_list("runes",rune_inv_data) + minetest.log("action","[runes] Rune inventory loaded for player " .. name) +end + +minetest.register_on_joinplayer(runes.functions.register_detached_inventory) + +runes.functions.save_detached_inventory = function(player) + local name = player:get_player_name() + local player_inv = player:get_inventory() + local datafile = io.open(minetest.get_worldpath().."/runes/"..name.."_rune.inv", "w") + local rune_inv_data = minetest.get_inventory({type = "detached", name=name.."_runes"}) + if not rune_inv_data then + minetest.log("error", "[runes] Couldn't save rune inventory for player " .. name .. " : data unavailable") + return false + end + local translated_rune_data = {} + for index, data in pairs(rune_inv_data:get_list("runes")) do + translated_rune_data[index] = data:get_name() .. " " .. data:get_count() + end + if datafile then + datafile:write(minetest.serialize(translated_rune_data)) + io.close(datafile) + end + minetest.log("action", "[runes] Rune inventory saved for player " .. name) +end + +minetest.register_on_leaveplayer(runes.functions.save_detached_inventory) +minetest.register_on_shutdown(function() + for index, player in pairs(minetest.get_connected_players()) do + runes.functions.save_detached_inventory(player) + end +end) diff --git a/api.md b/api.md new file mode 100755 index 0000000..b9a24e3 --- /dev/null +++ b/api.md @@ -0,0 +1,78 @@ +RUNES' API +========== + +(as accurate as possible) + +# 1. How it **does** work + + Every rune registered uses the same method to work and access its environment. Runes are first registered using a specific function. +Their datas are stored somewhere in a namespace (see below). You can use multiple values to indicate the needed amount of mana, the type of +the rune, its texture(s), etc. Then, once a rune is registered (as an item, for minetest), the most important thing is to connect its handler(s). +Another specific function is used to this, where you pass as an argument the reference to a function receiving the parameters of the item/node's callback +(eg. on_punch, on_dig, on_use, etc). The handler can choose to return "true" in order to manage the use of mana by itself instead of letting the +item's code in minetest.registered_items[rune].callback do it. + +# Functions + +#### `runes.register_rune(parameters)` +The most important function. This is the function you use to declare your rune and its specifications to minetest itself. +It takes one argument, a table. This table can have the following articles : + - `name` is mandatory, it's the rune's name (which will be used to craft its itemstring : runes:rune_name_level) + - `desc` is the item's description + - `img` is another table : + - ["minor"] = texture_file + - ["medium"] = texture_file + - ["major"] = texture_file + None of these parameters are mandatory. Runes can have 1 or 2 or 3 levels (ok, not implemented yet, but it's coming). + You can use a single value like `img = texture_file` and the texture will be applied for all levels + - `type` is the type of rune. At the moment, three types are available : + - "craftitem" will register a craftitem + - "cube" will register a node + - "plate" will register a little slab 0.1 node thin + - `needed_mana` is yet another table with different levels : + - ["minor"] = value + - ["medium"] = value + - ["major"] = value + When using this rune at level "major", the ["major"] value of mana will be taken by default if the player has enough mana, + or it will be forbidden for him to use the rune if he doesn't have enough mana. + You don't have to indicate that parameter and just let the handler manage the mana. + +#### `runes.functions.connect(itemname, callback, handler)` +This function will connect a registered rune's callback to its handler. It takes three mandatory arguments : + - `itemname` is the rune's identification name (aka. `name` for earlier). The same handler is used for every level + of a rune, so it has to determine what level he will handle (it's actually passed to it) + - `callback` is a code identifying the callback. It can be : + - `use` for `on_use` + - `place` for `after_place_node` + - `dig` for `after_dig_node` (actually commented due to bugs in this section) + - `can_dig` for `can_dig` (true logic) + - `punch` for `on_punch` (rather logic too) + - `handler` is the function handling the callback. See below for details. +Do not hesitate to consult lua_api.txt in order to see which callbacks are handled by what type of runes (craftitems, and nodes). + +#### `runes.functions.register_amulet(name, desc, maxcount, manadiff)` +Amulets are special items able to increase the maximum of mana you can stock. Here are the arguments expected to register an amulet : + - `name` is its name, used to create its itemstring : 'runes:name_amulet' + Don't feel forced to use '_amulet' in an amulet name, it's completly useless. + - `desc` is the item description. + - `maxcount` represents the maximum amount of this amulet you can have per stack (aka `stack_max`, see lua_api.txt for this). + - `manadiff` is the amount of space you will get to stock more mana in your inventory. A global_step loop is charged with the task + of regulary looking into everyone's rune inventory to calculate every player's max_mana. If you loose amulets, your mana_max +will dicrease (and mana may be lost with it). + +#### `runes.functions.register_detached_inventory(player)` +This function is only important for rune's inners. It registers a detached inventory to contain runes. Do not bother using it, there is no +actual use of it out of runes' core. + +#### `runes.functions.save_detached_inventory(player)` +Saves player's rune inventory on hard drive (in minetest.get_worldpath() .. "/runes/" .. player:get_player_name() .. "_rune.inv"). + +# Namespaces + +Here are a few namespaces that could be useful : + - `runes` global namespace, contains everything. Note : When [this](https://github.com/minetest/minetest/pull/2039) is merged, do what we have to do. + - `runes.datas` miscellaneous datas, including tables with parameters transmitted to the registration functions. + - `runes.datas.item`, the item's datas transmitted to registration + - `runes.datas.handlers`, handlers of everynodes (the index of this dictionnary is the rune's name) + - `runes.datas.amulets`, amulet's datas, used for the global_step loop and registration + - `runes.functions`, multiple useful functions diff --git a/depends.txt b/depends.txt new file mode 100755 index 0000000..66340fe --- /dev/null +++ b/depends.txt @@ -0,0 +1,3 @@ +default +unified_inventory +mana diff --git a/glyphs.lua b/glyphs.lua new file mode 100644 index 0000000..6d8ddfb --- /dev/null +++ b/glyphs.lua @@ -0,0 +1,403 @@ +-- A stylus to inscribe glyphs +-- Part of a Rune Mod Redo +-- By Mg, 2016 +-- License : WTFPL +-- + +runes.glyphs = {} + +minetest.register_tool("runes:stylus", { + description = "Stylus", + inventory_image = "runes_stylus.png", + on_use = function(itemstack, user, pointed_thing) + if not user or pointed_thing.type ~= "node" then return end + + local node = minetest.get_node_or_nil(pointed_thing.under) + if not node or not minetest.registered_nodes[node.name].walkable then + return + end + + node = minetest.get_node_or_nil(pointed_thing.above) + if not node or node.name ~= "air" then + return + end + + local main_inv = user:get_inventory():get_list("main") + local scroll = main_inv[user:get_wield_index()-1] + if not scroll then + minetest.chat_send_player(user:get_player_name(), "There is no scroll before the stylus in your inventory!") + return + elseif minetest.get_item_group(scroll:get_name(), "scroll") == 0 then + minetest.chat_send_player(user:get_player_name(), "The item before your stylus is not a scroll of knowledge!") + return + end + + local name = scroll:get_name():gsub("runes:scroll_", "") + if not runes.scrolls[name] then return end + local glyph = runes.scrolls[name].glyph + if not glyph then return end + + if mana.get(user:get_player_name()) < runes.glyphs[name].mana_cost then + minetest.chat_send_player(user:get_player_name(), "You need " .. runes.glyphs[name].mana_cost .. " of mana to inscribe that glyph") + return + end + + -- Calculate param2 manually since MineTest doesn't even do it + local diff = vector.subtract(pointed_thing.under, pointed_thing.above) + + minetest.add_node(pointed_thing.above, {name = glyph, param2 = minetest.dir_to_wallmounted(diff)}) + minetest.get_meta(pointed_thing.above):set_string("master", user:get_player_name()) + + itemstack:add_wear(65535 / 30) + mana.subtract(user:get_player_name(), runes.glyphs[name].mana_cost) + return itemstack + end, +}) + +minetest.register_craft({ + output = "runes:stylus", + recipe = { + {"", "default:obsidian_shard", "default:mithril_ingot"}, + {"default:obsidian_shard", "default:nyancat_rainbow", "default:obsidian_shard"}, + {"default:obsidian_shard", "default:obsidian_shard", ""}, + }, +}) + +minetest.register_craftitem("runes:recharge_wand", { + description = "Recharge wand", + inventory_image = "runes_recharge_wand.png", + on_use = function(itemstack, user, pointed_thing) + if not pointed_thing.type == "node" or not pointed_thing.under then + return + end + + local node = minetest.get_node_or_nil(pointed_thing.under) + if not node or not minetest.registered_nodes[node.name] + or minetest.get_item_group(node.name, "glyph") == 0 then + return + end + + local meta = minetest.get_meta(pointed_thing.under) + local charge = meta:get_int("charge") + local rname = node.name:sub(13) + local msg = "Rune already charged at maximum capacity" + + if charge < runes.glyphs[rname].max_charge then + local pmana = mana.get(user:get_player_name()) + -- Lower the index of pmana if it is higher than 20 (to simplify calculations) + if pmana > 20 then + pmana = 20 + end + local delta = runes.glyphs[rname].max_charge - charge + + if delta < pmana then + meta:set_int("charge", runes.glyphs[rname].max_charge) + mana.subtract(user:get_player_name(), delta) + msg = "Rune recharged at maximum capacity" + else + meta:set_int("charge", charge + pmana) + mana.subtract(user:get_player_name(), 20) + msg = "Rune recharged" + end + end + minetest.chat_send_player(user:get_player_name(), msg) + end, +}) + +minetest.register_craft({ + output = "runes:recharge_wand", + recipe = { + {"", "", "default:diamond"}, + {"", "default:mese_crystal_fragment", ""}, + {"default:stick", "", ""}, + }, +}) + +minetest.register_craftitem("runes:info_wand", { + description = "Information wand", + inventory_image = "runes_info_wand.png", + on_use = function(itemstack, user, pointed_thing) + if not pointed_thing.type == "node" then + return + end + + local node = minetest.get_node_or_nil(pointed_thing.under) + if not node or not minetest.registered_nodes[node.name] + or minetest.get_item_group(node.name, "glyph") == 0 then + return + end + + local meta = minetest.get_meta(pointed_thing.under) + local metas = meta:to_table().fields + local owner = meta:get_string("master") + if owner == "" then + owner = "nobody" + end + local rname = node.name:sub(13) + + local formspec = "size[7,7]" .. + "label[0,0; Rune informations :]" .. + "button_exit[3, 6.6; 1, 0.6; rune_info_exit; Exit]" .. + "textlist[0, 0.5; 6.8, 5.9; runes_info;" .. + "Rune : " .. rname .. "," .. + "Charge : " .. metas["charge"] .. "/" .. runes.glyphs[rname].max_charge .. "," .. + "Owner : " .. owner + local i = 4 + for field, value in pairs(metas) do + if field ~= "master" and field ~= "charge" then + formspec = formspec .. "," .. field .. " (meta) : " .. value + i = i + 1 + end + end + + formspec = formspec .. ";]" + + minetest.show_formspec(user:get_player_name(), "runes:glyph_info", formspec) + end, +}) + +minetest.register_craft({ + output = "runes:info_wand", + recipe = { + {"", "", "default:grass"}, + {"", "default:mithril_ingot", ""}, + {"default:stick", "", ""}, + }, +}) + +function register_glyph(name, basics, tab) + --[[ Basics can contain : + - texture = "runes_glyph_unknown.png", + - description = "Mysterious Glyph", + - initial_charge = 0 + - mana_cost = 0 + --]] + + runes.glyphs[name] = {} + runes.glyphs[name].mana_cost = basics.mana_cost or 0 + runes.glyphs[name].max_charge = basics.maximum_charge or 100 + + local def = table.copy(tab) + def.groups.glyph = 1 + + def.description = basics.description or "Mysterious Glyph" + def.inventory_image = basics.texture or "runes_glyph_unknown.png" + def.tiles = {basics.texture or "default_stone.png"} + def.on_construct = function(pos) + minetest.get_meta(pos):set_int("charge", (basics.initial_charge or 0)) + tab.on_construct(pos) + end + + def.drawtype = "signlike" + def.paramtype = "light" + def.paramtype2 = "wallmounted" + def.selection_box = { + type = "wallmounted", + wall_top = {-0.5, 0.4, -0.5, 0.5, 0.5, 0.5}, + wall_bottom = {-0.5, -0.5, -0.5, 0.5, -0.4, 0.5}, + wall_side = {-0.5, -0.5, -0.5, -0.4, 0.5, 0.5}, + } + def.walkable = false + + minetest.register_node("runes:glyph_" .. name, def) +end + + +register_glyph("watchdog", { + description = "Watch Dog Glyph", + texture = "runes_glyph_watchdog.png", + initial_charge = 300, + maximum_charge = 300, + mana_cost = 10, + }, { + light_source = 8, + groups = {snappy = 1}, + on_construct = function(pos) + minetest.get_node_timer(pos):start(0.2) + end, + on_timer = function(pos, elapsed) + local meta = minetest.get_meta(pos) + + for _, ref in pairs(minetest.get_objects_inside_radius(pos, 3)) do + if ref and not ref:get_armor_groups().immortal then + if not ref:is_player() or ref.is_fake_player or not meta:get_string("master") or meta:get_string("master") == "" or ref:get_player_name() ~= meta:get_string("master") then + ref:set_hp(ref:get_hp() - 1) + meta:set_int("charge", (meta:get_int("charge") or 1) - 1) + local collisionbox = ref:get_properties().collisionbox + local refpos = ref:getpos() + refpos.y = refpos.y + (((collisionbox[4] or 0) - (collisionbox[3] or 0)) / 2) + + local vel = vector.subtract(refpos, pos) + minetest.add_particlespawner({ + amount = 30, + minpos = pos, maxpos = pos, + minvel = vel, maxvel = vector.multiply(vel, 3), + minacc = 0, maxacc = 0,--vector.multiply(vel, 3), + minexptime = 1, maxexptime = 1, + minsize = 2, maxsize = 5, + collisiondetection = false, + vertical = false, + texture = "runes_glyph_watchdog.png", + }) + end + end + end + return true + end + } +) + +register_glyph("manasucker", { + description = "Mana Sucker Glyph", + texture = "runes_glyph_manasucker.png", + initial_charge = 100, + maximum_charge = 100, + mana_cost = 20, + }, { + groups = {snappy = 1}, + on_construct = function(pos) + minetest.get_node_timer(pos):start(3) + minetest.get_meta(pos):set_int("mana", 0) + end, + on_punch = function(pos, _, puncher) + local meta = minetest.get_meta(pos) + if meta:get_string("master") and puncher:is_player() and not puncher.is_fake_player and puncher:get_player_name() == meta:get_string("master") then + local k = meta:get_int("mana") + local name = puncher:get_player_name() + local o = mana.getmax(name) - mana.get(name) + local u = 0 + + if k > o then + u = k - mana.getmax(name) + mana.set(name, o) + else + mana.add(name, k) + end + + meta:set_int("mana", u) + end + end, + on_timer = function(pos, elapsed) + local meta = minetest.get_meta(pos) + if meta:get_int("charge") <= 0 then + return true + end + + local more_mana = 0 + for _, ref in pairs(minetest.get_objects_inside_radius(pos, 5)) do + if ref and ref:is_player() and not ref.is_fake_player and (not meta:get_string("master") or meta:get_string("master") == "" or meta:get_string("master") ~= ref:get_player_name()) then + local burst = math.random(10, 40) + local manalevel = mana.get(ref:get_player_name()) + + if manalevel > 0 then + if manalevel < burst then + mana.set(ref:get_player_name(), 0) + more_mana = more_mana + manalevel + else + mana.subtract(ref:get_player_name(), burst) + more_mana = more_mana + burst + end + + local collisionbox = ref:get_properties().collisionbox + local refpos = ref:getpos() + refpos.y = refpos.y + (((collisionbox[4] or 0) - (collisionbox[3] or 0)) / 2) + + local vel = vector.subtract(pos, refpos) + minetest.add_particlespawner({ + amount = 30, + minpos = refpos, maxpos = refpos, + minvel = vel, maxvel = vel, + minacc = 0, maxacc = 0,--vector.multiply(vel, 3), + minexptime = 1, maxexptime = 1, + minsize = 1, maxsize = 1, + collisiondetection = false, + vertical = false, + texture = "runes_glyph_manasucker.png", + }) + meta:set_int("charge", meta:get_int("charge") - 1) + end + end + end + meta:set_int("mana", meta:get_int("mana") + more_mana) + return true + end + } +) + +register_glyph("spontafire", { + description = "Spontaneous Fire Glyph", + texture = "runes_glyph_spontafire.png", + initial_charge = 0, + maximum_charge = 500, + mana_cost = 20, + }, { + groups = {snappy = 1}, + on_construct = function(pos) + minetest.get_node_timer(pos):start(1) + end, + on_timer = function(pos, elapsed) + local meta = minetest.get_meta(pos) + local charge = meta:get_int("charge") + + for _, ref in pairs(minetest.get_objects_inside_radius(pos, 10)) do + if ((not ref:is_player()) and ref:get_entity_name() ~= "gauges:hp_bar") + or (ref:get_player_name() ~= "" and ref:get_player_name() ~= meta:get_string("master")) then + local rpos = vector.round(ref:getpos()) + rpos.y = rpos.y - 1 + local node = minetest.get_node(rpos) + if node.name == "air" and (not minetest.is_protected(rpos, meta:get_string("master"))) + and charge >= runes.glyphs["spontafire"].mana_cost then + minetest.add_node(rpos, {name = "fire:basic_flame"}) + charge = charge - runes.glyphs["spontafire"].mana_cost + end + end + end + meta:set_int("charge", charge) + return true + end, +}) + +register_glyph("prankster", { + description = "Prankster Glyph", + texture = "runes_glyph_prankster.png", + initial_charge = 600, + maximum_charge = 1200, + mana_cost = 20, +},{ + groups = {snappy = 1}, + on_construct = function(pos) + minetest.get_node_timer(pos):start(5) + end, + on_timer = function(pos, elapsed) + local meta = minetest.get_meta(pos) + local charge = meta:get_int("charge") + + for _, ref in pairs(minetest.get_objects_inside_radius(pos, 10)) do + if charge >= runes.glyphs["prankster"].mana_cost and ref:is_player() and ref:get_player_name() ~= meta:get_string("master") then + local thieff = math.random(1,32) + local inv = ref:get_inventory() + if inv then + local stolen = inv:get_stack("main", thieff) + inv:set_stack("main", thieff, nil) + if stolen:get_count() > 0 then + local pos = ref:getpos() + local obj = minetest.add_item({x = pos.x, y = pos.y + 2.5, z = pos.z}, stolen) + if obj then + obj:setvelocity({x = math.random(-5,5), y = math.random(3,5), z = math.random(-5,5)}) + end + charge = charge - runes.glyphs["prankster"].mana_cost + minetest.chat_send_player(ref:get_player_name(), "The Prankster attacked you and stole " .. stolen:get_count() .. " " + .. (minetest.registered_items[stolen:get_name()].description or " of something") + ) + else + minetest.chat_send_player(ref:get_player_name(), "The Prankster attacked you but failed at stealing from you..") + end + else + minetest.log("Inventory retrieval failed") + end + end + end + meta:set_int("charge", charge) + return true + end, +}) diff --git a/handlers.lua b/handlers.lua new file mode 100755 index 0000000..82d26d1 --- /dev/null +++ b/handlers.lua @@ -0,0 +1,106 @@ +-- Use handlers for runes + +-- Every handler must receive as arguments the default callback arguments and +-- as first argument the power level of the rune as a string which can be : +-- `minor` : Low level +-- `medium` : Medium level +-- `major` : High level + +-- First, the functions + +projection = function(runelevel, itemstack, user, pointed_thing) + if pointed_thing.type == "object" then + local dir = vector.direction(user:getpos(),pointed_thing.ref:getpos()) + local v = pointed_thing.ref:getvelocity() or {x=0,y=0,z=0} + local ykb = 10 + if v.y ~= 0 then ykb = 0 end + pointed_thing.ref:setvelocity({x=dir.x*50,y=ykb,z=dir.z*50}) + end +end + +damage_around = function(runelevel, itemstack, user, pointed_thing) + for name,entity in pairs(minetest.get_objects_inside_radius(user:getpos(),10)) do + if true and (entity:is_player() and entity:get_player_name() ~= user:get_player_name()) then + entity:set_hp(1) + end + end +end + +earthquake = function(runelevel, itemstack, user, pointed_thing) + for name,entity in pairs(minetest.get_objects_inside_radius(user:getpos(),10)) do + local v = entity:getvelocity() or {x=0,y=0,z=0} + entity:setvelocity({x=v.x, y=v.y+50, z=v.z}) + end +end + +add_owner = function(runelevel, pos, placer, itemstack, pointed_thing) + if placer and placer:is_player() then + local meta = minetest.get_meta(pos) + meta:set_string("owner",placer:get_player_name()) + end +end + +is_owner_online = function(runelevel, pos) + local meta = minetest.get_meta(pos) + if meta:get_string("owner") ~= nil then + return minetest.get_player_by_name(meta:get_string("owner")) ~= nil + else + return false + end +end + +is_owner = function(runelevel, pos, player) + local meta = minetest.get_meta(pos) + if meta:get_string("owner") ~= nil and player:get_player_name() then + return meta:get_string("owner") == player:get_player_name() + else + return false + end +end + +go_to_me = function(runelevel, pos, node, digger) + if digger and is_owner_online(pos) and not (minetest.get_meta(pos):get_string("owner") == digger:get_player_name()) then + digger:setpos(minetest.get_player_by_name(minetest.get_meta(pos):get_string("owner")):getpos()) + mana.subtract(minetest.get_meta(pos):get_string("owner"), 5) + else + mana.add(digger:get_player_name(),50) + end +end + +set_manamax = function(runelevel, itemstack, user, pointed_thing) + if user and user:is_player() then + mana.set(user:get_player_name(),mana.getmax(user:get_player_name())) + if not minetest.get_player_privs(user:get_player_name()).server then + -- Violent reaction if not admin + user:set_hp(1) + user:set_breath(1) + local userpos = user:getpos() + local useritem = user:get_wielded_item() + user:setpos({x=userpos.x+math.random(-50,50),y = userpos.y + math.random(1,20),z = userpos.z + math.random(-50,50)}) + end + end +end + + +set_manamax = function(level, itemstack, user, pointed_thing) + if user and user:is_player() then + mana.set(user:get_player_name(),mana.getmax(user:get_player_name())) + if not minetest.get_player_privs(user:get_player_name()).server then + -- Violent reaction if not admin + user:set_hp(1) + user:set_breath(1) + local userpos = user:getpos() + local useritem = user:get_wielded_item() + user:setpos({x=userpos.x+math.random(-50,50),y = userpos.y + math.random(1,20),z = userpos.z + math.random(-50,50)}) + end + end +end + +-- Then, connect + +runes.functions.connect("project","use",projection) +runes.functions.connect("damager","use",damage_around) +runes.functions.connect("earthquake","use",earthquake) +runes.functions.connect("gotome","punch",go_to_me) +runes.functions.connect("gotome","can_dig",is_owner) +runes.functions.connect("megamana","use",set_manamax) diff --git a/init.lua b/init.lua new file mode 100755 index 0000000..209f939 --- /dev/null +++ b/init.lua @@ -0,0 +1,28 @@ +-- Rune mod by Mg +-- License GPLv3 + +local modpath = minetest.get_modpath("runes") + +runes = {} + +-- API first +dofile(modpath.."/api.lua") + +-- Then the rune themselves +dofile(modpath.."/registration.lua") + +-- The handlers +dofile(modpath.."/handlers.lua") + +-- The amulets +dofile(modpath.."/amulets.lua") + +---- From this point everything is redo ---- + +-- Stylus +dofile(modpath .. "/glyphs.lua") + +-- Scrolls +dofile(modpath .. "/scrolls.lua") + +minetest.log("action","[runes] Mod loaded") diff --git a/registration.lua b/registration.lua new file mode 100755 index 0000000..328c653 --- /dev/null +++ b/registration.lua @@ -0,0 +1,64 @@ +-- Rune definitions : registration.lua + +runes.datas.items = { + ["project"] = { + description = "Projection rune", + img = "runes_projection", + type = "craftitem", + needed_mana = { + ["minor"] = 15, + ["medium"] = 30, + ["major"] = 45, + }, + }, + ["damager"] = { + description = "Damaging rune", + img = "runes_damaging", + type = "craftitem", + needed_mana = { + ["minor"] = 180, + ["medium"] = 190, + ["major"] = 230 + } + }, + ["earthquake"] = { + description = "Earth Quake rune", + img = "runes_earthquake", + type = "craftitem", + needed_mana = { + ["minor"] = 70, + ["medium"] = 80, + ["major"] = 90 + } + }, + ["heal"] = { + description = "Healing rune", + img = "runes_heal", + type = "cube" + }, + ["gotome"] = { + description = "Go to me rune", + img = "runes_go_to_me", + type = "cube", + needed_mana = { + ["minor"] = 40, + ["medium"] = 50, + ["major"] = 75 + } + }, + ["megamana"] = { + description = "Mega Mana", + img = { + ["minor"] = "default_diamond.png", + ["medium"] = "default_diamond.png", + ["major"] = "default_diamond.png" + }, + type = "craftitem" + }, +} + +for key, value in pairs(runes.datas.items) do + local runereg = table.copy(value) + runereg.name = key + runes.functions.register_rune(runereg) +end diff --git a/scrolls.lua b/scrolls.lua new file mode 100644 index 0000000..d05d0e6 --- /dev/null +++ b/scrolls.lua @@ -0,0 +1,47 @@ +-- Scrolls for Runes Redo +-- + +runes.scrolls = {} + +function register_scroll(name, type, data) + if not data then return end + + local def = { + inventory_image = (data.texture or "runes_scroll_unknown.png"), + groups = {scroll = 1}, + description = (data.description or "Mysterious Scroll"), + } + + minetest.register_craftitem("runes:scroll_" .. name, def) + runes.scrolls[name] = {} + + if type == "knowledge" and data.glyph then + runes.scrolls[name].glyph = data.glyph + end +end + + + +register_scroll("watchdog", "knowledge", { + glyph = "runes:glyph_watchdog", + texture = "runes_scroll_watchdog.png", + description = "Watch Dog Knowledge Scroll" +}) + +register_scroll("manasucker", "knowledge", { + glyph = "runes:glyph_manasucker", + texture = "runes_scroll_manasucker.png", + description = "Mana Sucker Knowledge Scroll", +}) + +register_scroll("spontafire", "knowledge", { + glyph = "runes:glyph_spontafire", + texture = "runes_scroll_spontafire.png", + description = "Spontaneous Fire Knowledge Scroll", +}) + +register_scroll("prankster", "knowledge", { + glyph = "runes:glyph_prankster", + texture = "runes_scroll_prankster.png", + description = "Prankster Knowledge Scroll", +}) diff --git a/textures/runes_black_magic_block.png b/textures/runes_black_magic_block.png new file mode 100755 index 0000000..0717ce4 Binary files /dev/null and b/textures/runes_black_magic_block.png differ diff --git a/textures/runes_damaging_major.png b/textures/runes_damaging_major.png new file mode 100755 index 0000000..62cb009 Binary files /dev/null and b/textures/runes_damaging_major.png differ diff --git a/textures/runes_damaging_medium.png b/textures/runes_damaging_medium.png new file mode 100755 index 0000000..716d81f Binary files /dev/null and b/textures/runes_damaging_medium.png differ diff --git a/textures/runes_damaging_minor.png b/textures/runes_damaging_minor.png new file mode 100755 index 0000000..65fdfc6 Binary files /dev/null and b/textures/runes_damaging_minor.png differ diff --git a/textures/runes_earthquake_major.png b/textures/runes_earthquake_major.png new file mode 100755 index 0000000..46eca86 Binary files /dev/null and b/textures/runes_earthquake_major.png differ diff --git a/textures/runes_earthquake_medium.png b/textures/runes_earthquake_medium.png new file mode 100755 index 0000000..46eca86 Binary files /dev/null and b/textures/runes_earthquake_medium.png differ diff --git a/textures/runes_earthquake_minor.png b/textures/runes_earthquake_minor.png new file mode 100755 index 0000000..46eca86 Binary files /dev/null and b/textures/runes_earthquake_minor.png differ diff --git a/textures/runes_glyph_deus_ex_machina.png b/textures/runes_glyph_deus_ex_machina.png new file mode 100644 index 0000000..997da49 Binary files /dev/null and b/textures/runes_glyph_deus_ex_machina.png differ diff --git a/textures/runes_glyph_manasucker.png b/textures/runes_glyph_manasucker.png new file mode 100644 index 0000000..edcaf6b Binary files /dev/null and b/textures/runes_glyph_manasucker.png differ diff --git a/textures/runes_glyph_prankster.png b/textures/runes_glyph_prankster.png new file mode 100644 index 0000000..80476e2 Binary files /dev/null and b/textures/runes_glyph_prankster.png differ diff --git a/textures/runes_glyph_spontafire.png b/textures/runes_glyph_spontafire.png new file mode 100644 index 0000000..de19275 Binary files /dev/null and b/textures/runes_glyph_spontafire.png differ diff --git a/textures/runes_glyph_watchdog.png b/textures/runes_glyph_watchdog.png new file mode 100644 index 0000000..05b5953 Binary files /dev/null and b/textures/runes_glyph_watchdog.png differ diff --git a/textures/runes_go_to_me_major.png b/textures/runes_go_to_me_major.png new file mode 100755 index 0000000..c285844 Binary files /dev/null and b/textures/runes_go_to_me_major.png differ diff --git a/textures/runes_go_to_me_medium.png b/textures/runes_go_to_me_medium.png new file mode 100755 index 0000000..81fcbd7 Binary files /dev/null and b/textures/runes_go_to_me_medium.png differ diff --git a/textures/runes_go_to_me_minor.png b/textures/runes_go_to_me_minor.png new file mode 100755 index 0000000..6701fec Binary files /dev/null and b/textures/runes_go_to_me_minor.png differ diff --git a/textures/runes_grim_reaper_amulet.png b/textures/runes_grim_reaper_amulet.png new file mode 100755 index 0000000..9e68a34 Binary files /dev/null and b/textures/runes_grim_reaper_amulet.png differ diff --git a/textures/runes_heal_major.png b/textures/runes_heal_major.png new file mode 100755 index 0000000..aba4993 Binary files /dev/null and b/textures/runes_heal_major.png differ diff --git a/textures/runes_heal_medium.png b/textures/runes_heal_medium.png new file mode 100755 index 0000000..66ed41b Binary files /dev/null and b/textures/runes_heal_medium.png differ diff --git a/textures/runes_heal_minor.png b/textures/runes_heal_minor.png new file mode 100755 index 0000000..8d21ee8 Binary files /dev/null and b/textures/runes_heal_minor.png differ diff --git a/textures/runes_hellfire_amulet.png b/textures/runes_hellfire_amulet.png new file mode 100755 index 0000000..4e294d5 Binary files /dev/null and b/textures/runes_hellfire_amulet.png differ diff --git a/textures/runes_info_wand.png b/textures/runes_info_wand.png new file mode 100644 index 0000000..d4ffa44 Binary files /dev/null and b/textures/runes_info_wand.png differ diff --git a/textures/runes_megamana_major.png b/textures/runes_megamana_major.png new file mode 100755 index 0000000..169ae90 Binary files /dev/null and b/textures/runes_megamana_major.png differ diff --git a/textures/runes_megamana_medium.png b/textures/runes_megamana_medium.png new file mode 100755 index 0000000..6541366 Binary files /dev/null and b/textures/runes_megamana_medium.png differ diff --git a/textures/runes_megamana_minor.png b/textures/runes_megamana_minor.png new file mode 100755 index 0000000..121cbae Binary files /dev/null and b/textures/runes_megamana_minor.png differ diff --git a/textures/runes_minotaur_eye_amulet.png b/textures/runes_minotaur_eye_amulet.png new file mode 100755 index 0000000..930738b Binary files /dev/null and b/textures/runes_minotaur_eye_amulet.png differ diff --git a/textures/runes_recharge_wand.png b/textures/runes_recharge_wand.png new file mode 100644 index 0000000..4e21f4c Binary files /dev/null and b/textures/runes_recharge_wand.png differ diff --git a/textures/runes_rune_inventory.png b/textures/runes_rune_inventory.png new file mode 100755 index 0000000..58a93df Binary files /dev/null and b/textures/runes_rune_inventory.png differ diff --git a/textures/runes_scroll_deus_ex_machina.png b/textures/runes_scroll_deus_ex_machina.png new file mode 100644 index 0000000..edd9c73 Binary files /dev/null and b/textures/runes_scroll_deus_ex_machina.png differ diff --git a/textures/runes_scroll_manasucker.png b/textures/runes_scroll_manasucker.png new file mode 100644 index 0000000..24463dc Binary files /dev/null and b/textures/runes_scroll_manasucker.png differ diff --git a/textures/runes_scroll_prankster.png b/textures/runes_scroll_prankster.png new file mode 100644 index 0000000..ae0e88a Binary files /dev/null and b/textures/runes_scroll_prankster.png differ diff --git a/textures/runes_scroll_spontafire.png b/textures/runes_scroll_spontafire.png new file mode 100644 index 0000000..1cc9286 Binary files /dev/null and b/textures/runes_scroll_spontafire.png differ diff --git a/textures/runes_scroll_unknown.png b/textures/runes_scroll_unknown.png new file mode 100644 index 0000000..4de1979 Binary files /dev/null and b/textures/runes_scroll_unknown.png differ diff --git a/textures/runes_scroll_watchdog.png b/textures/runes_scroll_watchdog.png new file mode 100644 index 0000000..7b8d1dd Binary files /dev/null and b/textures/runes_scroll_watchdog.png differ diff --git a/textures/runes_silver_cross_amulet.png b/textures/runes_silver_cross_amulet.png new file mode 100755 index 0000000..b9c0bfc Binary files /dev/null and b/textures/runes_silver_cross_amulet.png differ diff --git a/textures/runes_stylus.png b/textures/runes_stylus.png new file mode 100644 index 0000000..e70b658 Binary files /dev/null and b/textures/runes_stylus.png differ