From 585cab107b93bb8e9d6d6beecd7489291c4a1817 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Sat, 12 Jan 2019 17:49:44 +0200 Subject: [PATCH] Initial commit --- chat.lua | 67 ++++++ description.txt | 1 + flatfile.lua | 0 init.lua | 94 +++++++++ mod.conf | 2 + regions.lua | 256 +++++++++++++++++++++++ settingtypes.txt | 40 ++++ textures/towny_visualize.png | Bin 0 -> 225 bytes town.lua | 393 +++++++++++++++++++++++++++++++++++ visualize.lua | 39 ++++ 10 files changed, 892 insertions(+) create mode 100644 chat.lua create mode 100644 description.txt create mode 100644 flatfile.lua create mode 100644 init.lua create mode 100644 mod.conf create mode 100644 regions.lua create mode 100644 settingtypes.txt create mode 100644 textures/towny_visualize.png create mode 100644 town.lua create mode 100644 visualize.lua diff --git a/chat.lua b/chat.lua new file mode 100644 index 0000000..c0a6b79 --- /dev/null +++ b/chat.lua @@ -0,0 +1,67 @@ + +-- Privileges + +minetest.register_privilege("towny", { + description = "Can create and join towns", +}) + +minetest.register_privilege("towny_admin", { + description = "Can administrate other people's towns", + give_to_singleplayer = false +}) + +-- Commands + +local function town_command (name, param) + if not minetest.get_player_by_name(name) then return false, "Can't run command on behalf of offline player." end + local pr1, pr2 = string.match(param, "^([%a%d_-]+) (.+)$") + local town = towny:get_player_town(name) + + -- Pre town requirement + + if (pr1 == "create" or pr1 == "new") and pr2 then + return towny:create_town(nil, name, pr2) + end + + if not town then + return false, "You are not currently in a town." + end + + -- Town management commands + local tdata = towny.towns[town] + + if param == "extend" or param == "claim" then + return towny:extend_town(nil, name) + elseif param == "leave" then + return towny:leave_town(name) + elseif param == "unclaim" then + return towny:abridge_town(nil, name) + elseif param == "visualize" then + towny.regions:visualize_town(town) + return true + elseif param == "delete" or param == "abandon" then + if towny.chat['delete_verify_' .. name] and pr2 == "I WANT TO DELETE MY TOWN" then + towny.chat['delete_verify_' .. name] = nil + return towny:delete_town(nil, name) + else + towny.chat['delete_verify_' .. name] = true + minetest.chat_send_player(name, minetest.colorize("#f79204", + "WARNING! Deleting your town will render ALL of the buildings in it without protection!")) + return false, "Please run the command again with 'I WANT TO DELETE MY TOWN' in all caps written after it." + end + end + + -- Plot management commands + if pr1 == "plot" then + local pl1, pl2 = string.match(pr2, "^([%a%d_-]+) (.+)$") + + end + + return false, "Invalid command usage." +end + +minetest.register_chatcommand("town", { + description = "Manage your town", + privs = {towny = true}, + func = town_command +}) diff --git a/description.txt b/description.txt new file mode 100644 index 0000000..b4ee168 --- /dev/null +++ b/description.txt @@ -0,0 +1 @@ +A township system for Minetest servers. diff --git a/flatfile.lua b/flatfile.lua new file mode 100644 index 0000000..e69de29 diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..a4203f1 --- /dev/null +++ b/init.lua @@ -0,0 +1,94 @@ +-- A township system for Minetest servers. +-- The MIT License - 2019 Evert "Diamond" Prants + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +towny = { + claimbonus = minetest.settings:get('towny_claim_bonus') or 8, + regions = { + size = minetest.settings:get('towny_claim_size') or 16, + maxclaims = minetest.settings:get('towny_claim_max') or 128, + distance = minetest.settings:get('towny_distance') or 80, + + -- Regions loaded into memory cache, see "Town regions data structure" + memloaded = {}, + }, + -- See "Town data structure" + towns = {}, + chat = {}, + + -- Set to true if files need to be updated + dirty = false, +} + +-- Town data structure +--[[ + town_id = { + name = "Town Name", + mayor = "Mayor name", + members = {}, + flags = {}, + plots = { + plot_id = { + owner = "Owner name", + members = {}, + flags = {} + } + } + } +]] + +-- Town regions data structure +--[[ + town_id = { + origin = , + blocks = { + { + x, y, x, -- Origin point for claim block + plot = nil -- Plot ID if this claim block is plotted + } + }, + } +]] + +-- Town-specific flags +--[[ + 'town_build' boolean lets everyone build in unplotted town claims + 'plot_build' boolean lets everyone build in unowned town plots + 'plot_member_build' boolean if false, plot members don't have build rights to plots by default + 'teleport' position town teleport point + 'pvp' boolean players can fight in the town if true, ignores server pvp settings + 'plot_pvp' boolean default plot pvp setting. defaults to false + 'joinable' boolean if true, anyone can join this town. defaults to false + 'greeting' string town's greeting message + 'plot_tax' float how much each plot costs each day (only with economy) + 'bank' float town's wealth (only with economy) (unchangeable by owner) + 'claim_blocks' int town's available claim blocks (unchangeable by owner) + 'origin' position town's center position, set at town creation (unchangeable by owner) +]] + +-- Members with flags +--[[ + 'plot_build' boolean if 'plot_member_build' town flag is false, + this one must be true for a plot member to be able to build on a plot. + If set to true in town flags, this member can build in all plots. + 'town_build' boolean if true, this member can build in town claims. + 'claim_create' boolean if true, this member can claim land for the town + 'claim_delete' boolean if true, this member can abandon claim blocks + 'plot_create' boolean if true, this member can create plots + 'plot_delete' boolean if true, this member can delete plots +]] + +-- Plot-specific flags +--[[ + 'teleport' position plot's teleport point + 'pvp' boolean players can fight here if true, ignores server pvp settings + 'cost' float plot cost (only with economy) + 'claimable' boolean is this plot available for claiming. if cost is more than 0, require payment + 'greeting' string plot's greeting message (defaults to "{owner}'s Plot"/"Unclaimed Plot") +]] + +dofile(modpath.."/flatfile.lua") +dofile(modpath.."/visualize.lua") +dofile(modpath.."/regions.lua") +dofile(modpath.."/town.lua") +dofile(modpath.."/chat.lua") diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..33cb3cc --- /dev/null +++ b/mod.conf @@ -0,0 +1,2 @@ +name = towny +description = A township system for Minetest servers. diff --git a/regions.lua b/regions.lua new file mode 100644 index 0000000..cc92dae --- /dev/null +++ b/regions.lua @@ -0,0 +1,256 @@ + +local main_is_protected = minetest.is_protected + +-- Calculate a region from the center point and radius +local function region_from_diameter(center, diameter) + local r = {x = diameter / 2, y = diameter / 2, z = diameter / 2} + return vector.subtract(center, r), vector.add(center, r) +end + +-- Test to see if a position is in a region +local function pos_in_region(pos, p1, p2) + return (pos.x >= p1.x and pos.y >= p1.y and pos.z >= p1.z) and + (pos.x <= p2.x and pos.y <= p2.y and pos.z <= p2.z) +end + +local function region_equal(v1, v2) + return (math.floor(v1.x) == math.floor(v2.x)) and + (math.floor(v1.y) == math.floor(v2.y)) and + (math.floor(v1.z) == math.floor(v2.z)) +end + +local function in_table(tbl, str) + for _,s in pairs(tbl) do + if s == str then + return true + end + end + return false +end + +-- Test to see if there's already a protected node in a region +function towny.regions:already_protected(p1, p2, name) + local found = false + for x = p1.x, p2.x do + if found then break end + for y = p1.y, p2.y do + if found then break end + for z = p1.z, p2.z do + if main_is_protected({x=x,y=y,z=z}, name) then + found = true + break + end + end + end + end + return found +end + +function towny.regions:build_perms(town, name, plotid) + if not towny.towns[town] then return true end -- Can build here, this town doesnt even exist + local towndata = towny.towns[town] + + -- Owner of the town can always build where they want in their town + if name == towndata.mayor then + return true + end + + -- Not even a town member, can't build here! + if not in_table(towndata.members, name) then return false end + + -- Plot build rights + if plotid and towndata.plots[plotid] then + -- This flag dictates that this member can build in all town plots, no matter if they own it or not + if towndata.members[name]['plot_build'] == true then return true end + + local plot = towndata.plots[plotid] + + -- This flag dictates that all members can build in unowned town plots + if not plot.owner and not towndata.flags['plot_build'] == true then return true end + + -- Plot owner can always build in their plot + if name == plot.owner then return true end + if in_table(plot.members, name) then + if towndata.flags['plot_member_build'] == false then + return plot.members[name]['plot_build'] == true + else + return true + end + end + else + -- This flag dictates that all members can build in unplotted town claims + if towndata.flags['town_build'] == true then return true end + + -- If this member has access to building in any town claims, let them + if towndata.members[name]['town_build'] == true then return true end + end + + return false +end + +local function single_range(p) + local p1,p2 + if p.x then + p1 = p + p2 = vector.subtract(p, {x=towny.regions.size,y=towny.regions.size,z=towny.regions.size}) + elseif #p == 2 then + p1 = p[1] + p2 = p[2] + end + return p1,p2 +end + +local function intest(town, name, plotid) + return not towny.regions:build_perms(town, name, plotid) +end + +function towny.regions:get_town_at(pos) + local in_town, in_plot, in_claim + for town,regions in pairs(towny.regions.memloaded) do + if in_town ~= nil then break end + if vector.distance(pos, regions.origin) <= towny.regions.size * towny.regions.maxclaims then + for _,tc in pairs(regions.blocks) do + local p1,p2 = single_range(tc) + if pos_in_region(pos,p1,p2) then + in_town = town + in_claim = {p1,p2} + if tc.plot then + in_plot = tc.plot + end + break + end + end + end + end + return in_town,in_plot,in_claim +end + +function towny.regions:get_closest_town(pos,name) + local in_town,block + local last_distance = 0 + for town,regions in pairs(towny.regions.memloaded) do + local count = true + + if name then + count = towny.regions:build_perms(town, name, nil) + end + + if count and vector.distance(pos, regions.origin) <= towny.regions.size * towny.regions.maxclaims then + for _,tc in pairs(regions.blocks) do + local p1,p2 = single_range(tc) + local dist = vector.distance(pos, p1) + if dist < last_distance or last_distance == 0 then + last_distance = dist + in_town = town + block = {p1,p2} + end + end + end + end + return in_town,block,last_distance +end + +function towny.regions:town_claim_exists(town,p1) + if not towny.regions.memloaded[town] then return false end + local blocks = towny.regions.memloaded[town].blocks + for _,pos in pairs(blocks) do + if region_equal(p1, pos) then return true end + end + return false +end + +function towny.regions:align_new_claim_block(pos,name) + local r = towny.regions.size + local closest_town,closest_block,distance = towny.regions:get_closest_town(pos,name) + if not closest_town then return nil end + if distance > (r * 2) then return nil end -- Too far + + local new_pos + local p1,p2 = closest_block[1],closest_block[2] + + -- X + if (pos.z <= p1.z and pos.z >= p2.z) and (p1.y >= pos.y and p2.y <= pos.y) then + if pos.x > p1.x then + new_pos = vector.add(p1, {x=r,y=0,z=0}) + else + new_pos = vector.add(p1, {x=-r,y=0,z=0}) + end + -- Y + elseif (pos.x <= p1.x and pos.x >= p2.x) and (pos.z <= p1.z and pos.z >= p2.z) then + if pos.y > p1.y then + new_pos = vector.add(p1, {x=0,y=r,z=0}) + else + new_pos = vector.add(p1, {x=0,y=-r,z=0}) + end + -- Z + elseif (pos.x <= p1.x and pos.x >= p2.x) and (p1.y >= pos.y and p2.y <= pos.y) then + if pos.z > p1.z then + new_pos = vector.add(p1, {x=0,y=0,z=r}) + else + new_pos = vector.add(p1, {x=0,y=0,z=-r}) + end + end + + if new_pos == nil then return nil end -- Impossible position + return new_pos,closest_town +end + +function towny.regions:remove_claim(p1,town) + local blocks = {} + if not towny.regions.memloaded[town] then return false, "This town does not exist anymore." end + for _,pos in pairs(towny.regions.memloaded[town].blocks) do + if pos['plot'] and towny.towns[town].plots[pos['plot']] then + return false, "This town claim defines a plot. Please remove the plot before removing the claim!" + elseif region_equal(p1, pos) and pos['origin'] == true then + return false, "This town claim is the origin of this town!" + elseif not region_equal(p1, pos) then + table.insert(blocks, pos) + end + end + + towny.regions.memloaded[town].blocks = blocks + return true +end + +function towny.regions:set_plot(pos,town,plot) + if not towny.regions.memloaded[town] then return false, "This town does not exist anymore." end + for _,block in pairs(towny.regions.memloaded[town].blocks) do + if region_equal(block, pos) then + block['plot'] = plot + break + end + end + return true +end + +function towny.regions:visualize_town(town) + if not towny.regions.memloaded[town] then return end + for _,pos in pairs(towny.regions.memloaded[town].blocks) do + towny.regions:visualize_radius(vector.subtract(pos, + {x=towny.regions.size/2,y=towny.regions.size/2,z=towny.regions.size/2})) + end +end + +function towny.regions:position_protected_from(pos, name) + local town,plot = towny.regions:get_town_at(pos) + if not town then return false end + + return intest(town, name, plot) +end + +-- Finally, override is_protected +function minetest.is_protected(pos, name) + local bt = towny.regions:position_protected_from(pos, name) + if bt ~= false then + return true + end + + return main_is_protected(pos, name) +end + +--[[if minetest.settings:get('towny_prevent_protector') == 'true' then + minetest.register_on_placenode(function (pos, newnode, placer, oldnode, itemstack, pointed_thing) + local town = towny.regions:get_town_at(pos) + if not town return end + end) +end]] diff --git a/settingtypes.txt b/settingtypes.txt new file mode 100644 index 0000000..3485ca7 --- /dev/null +++ b/settingtypes.txt @@ -0,0 +1,40 @@ + +# Claims settings +################## + +# Towny claim diameter +towny_claim_size (Claim size) int 16 + +# Towny max claim blocks +towny_claim_max (Max town claims) int 128 + +# Towny max claim blocks +towny_claim_bonus (Claim blocks added per new member) int 8 + +# Minimum distance between towns (in claim blocks) +towny_distance (Max town claims) int 80 + +# Prevent protectors from being placed in a town +# Towns provide their own protection +# Recommended to be kept as true, may cause issues with claims otherwise +towny_prevent_protector (Prevent protectors from being placed in a town) bool true + +# Chat settings +################ + +# Set to false if you have any other mods that alter the way player-sent messages look +towny_chat (Allow towny to modify the chat) bool true + +# If true, players must be invited into towns (No direct joining) +towny_questionaire (Invite-based membership) bool true + +# Other settings +################# + +towny_eco (Enable economy) bool false +towny_tax (Enable taxation) bool false + +# Units depend on economy mod used in server +towny_create_cost (Town creation cost) int 10000 +towny_claim_cost (Town claim block cost) int 1000 +towny_upkeep_cost (Town upkeep cost, multiplied by member count) int 10 diff --git a/textures/towny_visualize.png b/textures/towny_visualize.png new file mode 100644 index 0000000000000000000000000000000000000000..93ac61711ce760dcb9cf6c6dd343ac6d3c3b7ff0 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85s1GL71^(seKtxkiEpy*OmP-BM&>fLe! towny.regions.distance * towny.regions.size and not towny_admin then + return err_msg(player, "This location is too close to another town!") + end + + if towny:get_town_by_name(name) and not towny_admin then + return err_msg(player, "A town by this name already exists!") + end + + -- TODO: Economy + + -- New town information + local p1 = vector.add(pos, {x=tr / 2,y=tr - 1,z=tr / 2}) + local p2 = vector.subtract(pos, {x=tr / 2,y=1,z=tr / 2}) + local id = minetest.hash_node_position(pos) + local data = { + name = name, + mayor = player, + members = { + [player] = {["town_build"] = true, ["plot_build"] = true} + }, + plots = {}, + flags = { + origin = pos, + claim_blocks = towny.claimbonus, + plot_member_build = true, + } + } + + local regions = { + origin = pos, + blocks = { + { x=p1.x, y=p1.y, z=p1.z, origin = true } + } + } + + towny.towns[id] = data + towny.regions.memloaded[id] = regions + towny.dirty = true + + minetest.chat_send_player(player, "Your town has successfully been founded!") + minetest.chat_send_all(player .. " has started a new town called '" .. name .. "'!") + + towny.regions:visualize_area(p1,p2) + + return true +end + +function towny:extend_town(pos,player) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town then + return err_msg(player, "You're not currently in a town!") + end + + local data = towny.towns[town] + if data.mayor ~= player and data.members[player]['claim_create'] ~= true then + return err_msg(player, "You do not have permission to spend claim blocks in your town.") + end + + if data.flags["claim_blocks"] < 1 then + return err_msg(player, "You do not have enough remaining claim blocks!") + end + + local p1,closest_town = towny.regions:align_new_claim_block(pos, player) + if not p1 then + return err_msg(player, "You cannot claim this area! Town blocks must be aligned side-by-side.") + end + + if towny.regions:town_claim_exists(town,p1) then + return err_msg(player, "This area is already claimed.") + end + + if closest_town ~= town then + return err_msg(player, "Something went wrong!") + end + + table.insert(towny.regions.memloaded[town].blocks, p1) + data.flags["claim_blocks"] = data.flags["claim_blocks"] - 1 + minetest.chat_send_player(player, "Successfully claimed this block!") + towny.dirty = true + + towny.regions:visualize_radius(vector.subtract(p1, {x=tr/2,y=tr/2,z=tr/2})) + return true +end + +function towny:abridge_town(pos,player) + local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town and not towny_admin then + return err_msg(player, "You're not currently in a town!") + end + + local data = towny.towns[town] + if data.mayor ~= player and data.members[player]['claim_delete'] ~= true and not towny_admin then + return err_msg(player, "You do not have permission to delete claim blocks in your town.") + end + + local t,p,c = towny.regions:get_town_at(pos) + if not t or (t ~= town and not towny_admin) then + return err_msg(player, "You are not in any town you can modify.") + end + + local success,message = towny.regions:remove_claim(c,t) + if not success then + return err_msg(player, "Failed to abandon claim block: " .. message) + end + + table.insert(towny.regions.memloaded[t].blocks, p1) + data.flags["claim_blocks"] = data.flags["claim_blocks"] + 1 + minetest.chat_send_player(player, "Successfully abandoned this claim block!") + towny.dirty = true + + return true +end + +function towny:leave_town(player) + local town = towny:get_player_town(player) + if not town then + return err_msg(player, "You're not currently in a town!") + end + + local data = towny.towns[town] + if data.mayor == player then + return err_msg(player, "You cannot abandon a town that you own! Either delete the town or transfer mayorship.") + end + + -- Update town members + local members = {} + for member,mdata in pairs(data.members) do + if member ~= player then + members[member] = mdata + end + end + data.members = members + + -- Update plot members + for plotid,pdata in pairs(data.plots) do + local members = {} + if pdata.owner == player then + pdata.owner = nil + if pdata.flags["greeting"] ~= nil then + pdata.flags["greeting"] = nil + end + end + + for mem,dat in pairs(pdata.members) do + if mem ~= player then + -- Transfer ownership to the first other member + if pdata.owner == nil then + pdata.owner = mem + end + members[mem] = dat + end + end + pdata.members = members + end + + towny.dirty = true + minetest.chat_send_player(player, "You successfully left the town.") + return true +end + +function towny:delete_town(pos,player) + local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town and not towny_admin then + return err_msg(player, "You're not currently in a town!") + end + + local t,p,c = towny.regions:get_town_at(pos) + if not t or (t ~= town and not towny_admin) then + return err_msg(player, "You are not in any town you can modify.") + end + + local data = towny.towns[t] + if data.mayor ~= player and not towny_admin then + return err_msg(player, "You do not have permission to delete this town.") + end + + local name = data.name .. "" + + -- Wipe the town + towny.towns[t] = nil + towny.regions.memloaded[t] = nil + towny.dirty = true + + minetest.chat_send_player(player, "Successfully deleted the town!") + minetest.chat_send_all("The town '" .. name .. "' has fell into ruin.") + return true +end + +function towny:delete_plot(pos,player) + local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town and not towny_admin then + return err_msg(player, "You're not currently in a town!") + end + + local t,p,c = towny.regions:get_town_at(pos) + if not t or (t ~= town or not towny_admin) then + return err_msg(player, "You are not in any town you can modify.") + end + + local data = towny.towns[t] + local plot_data = data.plots[p] + if (data.mayor ~= player and data.members[player]['plot_delete'] ~= true) and (plot_data.owner ~= player) and not towny_admin then + return err_msg(player, "You do not have permission to delete this plot.") + end + + towny.regions:set_plot(c,t,nil) + data.plots[p] = nil + towny.dirty = true + + minetest.chat_send_player(player, "Successfully removed the plot.") + return true +end + +function towny:create_plot(pos,player) + local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town and not towny_admin then + return err_msg(player, "You're not currently in a town!") + end + + local t,p,c = towny.regions:get_town_at(pos) + if not t or (t ~= town and not towny_admin) then + return err_msg(player, "You are not in any town you can modify.") + end + + if p ~= nil then + return err_msg(player, "You cannot create a plot here!") + end + + local data = towny.towns[t] + if data.mayor ~= player and data.members[player]['plot_create'] ~= true and not towny_admin then + return err_msg(player, "You do not have permission to create plots in this town.") + end + + local pid = minetest.hash_node_position(c) + + local success,message = towny.regions:set_plot(c,t,pid) + if not success then + minetest.chat_send_player(player, "Failed to create a plot here: " .. message) + return false + end + + data.plots[pid] = { + owner = player, + members = {[player] = {}}, + flags = {}, + } + towny.dirty = true + + minetest.chat_send_player(player, "Successfully created a plot!") + towny.regions:visualize_radius(vector.subtract(c, {x=tr/2,y=tr/2,z=tr/2})) + return true +end + +function towny:set_plot_flags(pos,player,flag,value) + local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town and not towny_admin then + return err_msg(player, "You're not currently in a town!") + end + + local t,p,c = towny.regions:get_town_at(pos) + if not t or (t ~= town and not towny_admin) then + return err_msg(player, "You are not in any town you can modify.") + end + + if p ~= nil then + return err_msg(player, "There is no plot here! Please stand in the plot you wish to modify.") + end + + local data = towny.towns[t] + local plot_data = data.plots[p] + if data.mayor ~= player and plot_data.owner ~= player and not towny_admin then + return err_msg(player, "You do not have permission to modify this plot.") + end + + minetest.chat_send_player(player, "Successfully set the plot flag '" .. flag .."' to '" .. value .. "'!") + if type(value) == "string" and minetest.string_to_pos(value) then + value = minetest.string_to_pos(value) + end + plot_data.flags[flag] = value +end + +function towny:set_town_flags(pos,player,flag,value) + local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) + if not pos then + pos = minetest.get_player_by_name(player):get_pos() + end + + local town = towny:get_player_town(player) + if not town and not towny_admin then + return err_msg(player, "You're not currently in a town!") + end + + local t,p,c = towny.regions:get_town_at(pos) + if not t or (t ~= town and not towny_admin) then + return err_msg(player, "You are not in any town you can modify.") + end + + local data = towny.towns[t] + if data.mayor ~= player and not towny_admin then + return err_msg(player, "You do not have permission to modify this town.") + end + + if (flag == 'bank' or flag == 'claim_blocks' or flag == 'origin') and not towny_admin then + return err_msg(player, "You cannot change this flag.") + end + + minetest.chat_send_player(player, "Successfully set the town flag '" .. flag .."' to '" .. value .. "'!") + if type(value) == "string" and minetest.string_to_pos(value) then + value = minetest.string_to_pos(value) + end + data.flags[flag] = value +end + +function towny:get_claims_total(town) + if not towny.regions.memloaded[town] then return 0 end + return #towny.regions.memloaded[town].blocks +end diff --git a/visualize.lua b/visualize.lua new file mode 100644 index 0000000..4cfcc2c --- /dev/null +++ b/visualize.lua @@ -0,0 +1,39 @@ +-- Visualize an area + +local r1 = towny.regions.size + 1 +local c_obj_props = { + hp = 1, + glow = 1, + physical = false, + pointable = true, + visual = "cube", + visual_size = {x = r1, y = r1}, + textures = {"towny_visualize.png","towny_visualize.png","towny_visualize.png", + "towny_visualize.png","towny_visualize.png","towny_visualize.png"}, + static_save = false, + use_texture_alpha = true, +} + +minetest.register_entity("towny:region_visual", { + initial_properties = c_obj_props, + on_punch = function(self) + self.object:remove() + end, + timer0 = 0, + on_step = function (self,dt) + self.timer0 = self.timer0 + 1 + if self.timer0 > 600 then + self.object:remove() + end + end +}) + +function towny.regions:visualize_radius(pos) + local e = minetest.add_entity(pos, "towny:region_visual") +end + +function towny.regions:visualize_area(p1,p2) + local r = towny.regions.size / 2 + local center = {x=p2.x + r,y=p2.y + r,z=p2.z + r} + local e = minetest.add_entity(center, "towny:region_visual") +end