commit 585cab107b93bb8e9d6d6beecd7489291c4a1817 Author: Evert Prants Date: Sat Jan 12 17:49:44 2019 +0200 Initial commit 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 0000000..93ac617 Binary files /dev/null and b/textures/towny_visualize.png differ diff --git a/town.lua b/town.lua new file mode 100644 index 0000000..b306cf4 --- /dev/null +++ b/town.lua @@ -0,0 +1,393 @@ + +local tr = towny.regions.size +local function in_table(tbl, str) + for _,s in pairs(tbl) do + if s == str then + return true + end + end + return false +end + +local function err_msg(player, msg) + minetest.chat_send_player(player, minetest.colorize("#ff1111", msg)) + return false +end + +function towny:get_player_town(name) + for town,data in pairs(towny.towns) do + if data.mayor == name then + return town + elseif in_table(data.members, name) then + return town + end + end + return nil +end + +function towny:get_town_by_name(name) + for town,data in pairs(towny.towns) do + if data.name.lower() == name.lower() then + return town + end + end + return nil +end + +function towny:create_town(pos, player, name) + 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 + + if towny:get_player_town(player) then + return err_msg(player, "You're already in a town! Please leave your current town before founding a new one!") + end + + local _,__,distance = towny.regions:get_closest_town(pos) + if distance > 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