diff --git a/chat.lua b/chat.lua index 2b15ace..c5c296f 100644 --- a/chat.lua +++ b/chat.lua @@ -18,11 +18,10 @@ minetest.register_privilege("towny_admin", { -- Send message to all town members who are online function towny.chat.announce_to_members(town,message) local tdata = towny.towns[town] - if tdata then - for member in pairs(tdata.members) do - if minetest.get_player_by_name(member) then - minetest.chat_send_player(member,message) - end + if tdata then return end + for member in pairs(tdata.members) do + if minetest.get_player_by_name(member) then + minetest.chat_send_player(member,message) end end end @@ -50,8 +49,7 @@ local function invite_player(town,player,target) local tdata = towny.towns[town] - minetest.chat_send_player(target, ("You have been invited to join town '%s' by %s") - :format(tdata.name, player)) + minetest.chat_send_player(target, ("You have been invited to join town '%s' by %s"):format(tdata.name, player)) minetest.chat_send_player(target, "You can accept this invite by typing '/town invite accept' or deny '/town invite deny'") towny.chat.invites[town.."-"..target] = { rejected = false, town = town, player = target, invited = player } @@ -62,7 +60,7 @@ local function join_town(town,player,from_invite) local tdata = towny.towns[town] if not tdata then return false, "No such town" end if (not from_invite and not tdata.flags['joinable']) then return false, "You cannot join this town." end - towny.chat.announce_to_members(town, minetest.colorize("#02aacc", player.." has joined the town!")) + towny.chat.announce_to_members(town, minetest.colorize("#02aacc", ("%s has joined the town!"):format(player))) minetest.chat_send_player(player, ("You have successfully joined the town '%s'!"):format(tdata.name)) tdata.members[player] = {} towny.mark_dirty(town,false) @@ -92,7 +90,7 @@ local function invite_respond(player,response) return false, "You do not have any pending invites." end -local function send_flags (player,flags,message) +local function send_flags (flags,message) local shiny = {} for flag,value in pairs(flags) do if type(value) == "table" then @@ -113,7 +111,9 @@ local function send_flags (player,flags,message) end 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 player = minetest.get_player_by_name(name) + if not player 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) @@ -127,11 +127,10 @@ local function town_command (name, param) elseif pr1 == "join" and towny.get_town_by_name(pr2) and not town then return join_town(pr2,name,false) elseif pr1 == "show" or pr1 == "info" then - if towny.get_town_by_name(pr2) then - town_info = pr2 - else + if not towny.get_town_by_name(pr2) then return false, "No such town." end + town_info = pr2 elseif param == "" and town then town_info = town end @@ -152,6 +151,11 @@ local function town_command (name, param) return towny.extend_town(nil, name) elseif param == "leave" then return towny.leave_town(name) + elseif param == "teleport" then + local portal = tdata.flags['teleport'] + if not portal then portal = tdata.flags['origin'] end + player:set_pos(portal) + return true, "Teleporting you to town.." elseif param == "unclaim" then return towny.abridge_town(nil, name) elseif param == "visualize" then @@ -160,7 +164,7 @@ local function town_command (name, param) elseif param == "flags" then local flags = towny.get_flags(town) if flags then - return send_flags(player,flags,"Flags of your town") + return send_flags(flags,"Flags of your town") end elseif (param == "delete" or param == "abandon") or (pr1 == "delete" or pr1 == "abandon") then if towny.chat['delete_verify_' .. name] and pr2 == "I WANT TO DELETE MY TOWN" then @@ -206,7 +210,7 @@ local function town_command (name, param) elseif pr2 == "flags" then local flags = towny.get_plot_flags(town,nil,name) if flags then - return send_flags(player,flags,"Flags of this plot") + return send_flags(flags,"Flags of this plot") else return false, "There's no plot here." end diff --git a/init.lua b/init.lua index a69734b..7c57bfd 100644 --- a/init.lua +++ b/init.lua @@ -11,19 +11,18 @@ towny = { regions = { size = tonumber(minetest.settings:get('towny_claim_size')) or 16, height = tonumber(minetest.settings:get('towny_claim_height')) or 64, - maxclaims = tonumber(minetest.settings:get('towny_claim_max')) or 128, distance = tonumber(minetest.settings:get('towny_distance')) or 80, -- Regions loaded into memory cache, see "Town regions data structure" memloaded = {}, }, -- See "Town data structure" - storage = {}, - towns = {}, - chat = { - chatmod = (minetest.settings:get('towny_chat') == "true") or true, - questionaire = (minetest.settings:get('towny_questionaire') == "true") or true, - invites = {}, + storage = {}, + towns = {}, + chat = { + chatmod = minetest.settings:get_bool('towny_chat', true), + invite = minetest.settings:get_bool('towny_invite', true), + invites = {}, }, levels = { { @@ -75,25 +74,26 @@ towny = { }, flags = { town = { - ['town_build'] = {"boolean", "lets everyone build in unplotted town claims"}, - ['plot_build'] = {"boolean", "lets everyone build in unowned town plots"}, + ['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'] = {"vector", "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"}, - ['tax'] = {"number", "how much each member has to pay each day to stay in town"}, - ['bank'] = {"number", "town's treasury", false}, - ['claim_blocks'] = {"number", "town's bonus claim blocks", false}, - ['origin'] = {"vector", "town's center position, set at town creation", false}, + ['teleport'] = {"vector", "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"}, + ['tax'] = {"number", "how much each member has to pay each day to stay in town"}, + ['mayor'] = {"member", "town's mayor"}, + ['bank'] = {"number", "town's treasury", false}, + ['claim_blocks'] = {"number", "town's bonus claim blocks", false}, + ['origin'] = {"vector", "town's center position, set at town creation", false}, }, town_member = { - ['town_build'] = {"boolean", "member can build in unplotted town claims"}, - ['claim_create'] = {"boolean", "member can claim land for the town"}, - ['claim_delete'] = {"boolean", "member can abandon claim blocks"}, - ['plot_create'] = {"boolean", "member can create plots"}, - ['plot_delete'] = {"boolean", "member can delete plots"}, + ['town_build'] = {"boolean", "member can build in unplotted town claims"}, + ['claim_create'] = {"boolean", "member can claim land for the town"}, + ['claim_delete'] = {"boolean", "member can abandon claim blocks"}, + ['plot_create'] = {"boolean", "member can create plots"}, + ['plot_delete'] = {"boolean", "member can delete plots"}, }, plot = { ['teleport'] = {"vector", "plot's teleport point"}, @@ -112,11 +112,10 @@ towny = { dirty = false, } --- Town data structure --[[ + -- Town data structure town_id = { name = "Town Name", - mayor = "Mayor name", members = {}, flags = {}, plots = { @@ -127,16 +126,15 @@ towny = { } } } -]] --- Town regions data structure ---[[ + -- 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 + origin = true -- Center of town, if present } }, } diff --git a/regions.lua b/regions.lua index a9f40c6..38e39e4 100644 --- a/regions.lua +++ b/regions.lua @@ -37,7 +37,7 @@ function towny.regions.build_perms(town, name, plotid) local towndata = towny.towns[town] -- Owner of the town can always build where they want in their town - if name == towndata.mayor then + if name == towndata.flags['mayor'] then return true end @@ -74,7 +74,8 @@ function towny.regions.build_perms(town, name, plotid) return false end -local function single_range(p) +-- Ensure double coordinates for a range +function towny.regions.ensure_range(p) local p1,p2 if p.x then p1 = p @@ -90,9 +91,9 @@ 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 + if vector.distance(pos, regions.origin) <= towny.regions.size * 448 then for _,tc in pairs(regions.blocks) do - local p1,p2 = single_range(tc) + local p1,p2 = towny.regions.ensure_range(tc) if pos_in_region(pos,p1,p2) then in_town = town in_claim = {p1,p2} @@ -117,9 +118,9 @@ function towny.regions.get_closest_town(pos,name) count = towny.regions.build_perms(town, name, nil) end - if count and vector.distance(pos, regions.origin) <= towny.regions.size * towny.regions.maxclaims then + if count and vector.distance(pos, regions.origin) <= towny.regions.size * 448 then for _,tc in pairs(regions.blocks) do - local p1,p2 = single_range(tc) + local p1,p2 = towny.regions.ensure_range(tc) local center = vector.subtract(p1, {x=tr/2,y=th/2,z=tr/2}) local dist = vector.distance(pos, center) if dist < last_distance or last_distance == 0 then @@ -208,7 +209,7 @@ 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 - local p1,p2 = single_range(pos) + local p1,p2 = towny.regions.ensure_range(pos) towny.regions.visualize_area(p1,p2) end end diff --git a/settingtypes.txt b/settingtypes.txt index 5baa97d..2a38415 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -15,9 +15,6 @@ towny_claim_size (Claim size) int 16 # Towny claim height towny_claim_size (Claim height) int 64 -# Towny max claim blocks -towny_claim_max (Max town claims) int 128 - # Minimum distance between towns (in claim blocks) towny_distance (Max town claims) int 80 @@ -33,13 +30,13 @@ towny_prevent_protector (Prevent protectors from being placed in a town) bool tr 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 +towny_invite (Invite-based membership) bool true -# Other settings -################# +# Economy settings +################### towny_eco (Enable economy) bool false -towny_tax (Enable taxation) bool false +towny_tax (Allow taxation) bool true # Units depend on economy mod used in server towny_create_cost (Town creation cost) int 10000 diff --git a/town.lua b/town.lua index 5adc6bf..bdde4a4 100644 --- a/town.lua +++ b/town.lua @@ -31,9 +31,8 @@ local function flag_typeify(value,pos) return value end -local function flag_validity(flag,scope,value,pos) +local function flag_validity(flag,scope,value,pos,members) value = flag_typeify(value,pos) - if value == nil then return true end local spd = towny.flags[scope] if type(spd[flag]) == "string" then flag = spd[flag] @@ -41,10 +40,15 @@ local function flag_validity(flag,scope,value,pos) if not spd[flag] then return false end if spd[flag][3] == false then return false end + local flgtype = spd[flag][1] - if spd[flag][1] == "vector" and (not value.x or not value.y or not value.z) then + if flgtype == "member" and (members and not members[tostring(value)]) then return false - elseif spd[flag][1] ~= "vary" and type(value) ~= spd[flag][1] then + elseif flgtype == "member" and value == nil then + return false + elseif flgtype == "vector" and (value and (not value.x or not value.y or not value.z)) then + return false + elseif (flgtype == "string" or flgtype == "number") and type(value) ~= flgtype then return false end @@ -53,7 +57,7 @@ end function towny.get_player_town(name) for town,data in pairs(towny.towns) do - if data.mayor == name then + if data.flags['mayor'] == name then return town elseif data.members[name] then return town @@ -107,15 +111,15 @@ function towny.create_town(pos, player, name) local id = minetest.sha1(minetest.hash_node_position(pos)) local data = { name = name, - mayor = player, members = { [player] = {} }, plots = {}, flags = { + mayor = player, origin = pos, claim_blocks = towny.claimbonus, - plot_member_build = true, + plot_member_build = true } } @@ -149,7 +153,7 @@ function towny.extend_town(pos,player) end local data = towny.towns[town] - if data.mayor ~= player and data.members[player]['claim_create'] ~= true then + if data.flags['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 @@ -174,7 +178,8 @@ function towny.extend_town(pos,player) minetest.chat_send_player(player, ("Successfully claimed this block! You have %d claim blocks left!"):format(towny.get_claims_available(town))) towny.mark_dirty(town, true) - towny.regions.visualize_radius(vector.subtract(p1, {x=tr/2,y=th/2,z=tr/2})) + local p1,p2 = towny.regions.ensure_range(p1) + towny.regions.visualize_area(p1,p2) return true end @@ -190,7 +195,7 @@ function towny.abridge_town(pos,player) end local data = towny.towns[town] - if data.mayor ~= player and data.members[player]['claim_delete'] ~= true and not towny_admin then + if data.flags['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 @@ -218,7 +223,7 @@ function towny.leave_town(player,kick) end local data = towny.towns[town] - if data.mayor == player then + if data.flags['mayor'] == player then return err_msg(player, "You cannot abandon a town that you own! Either delete the town or transfer mayorship.") end @@ -267,15 +272,15 @@ function towny.kick_member(town,player,member) local towny_admin = minetest.check_player_privs(player, { towny_admin = true }) local data = towny.towns[town] - if data.mayor ~= player and not towny_admin then + if data.flags['mayor'] ~= player and not towny_admin then return err_msg(player, "You do not have permission to kick people from this town.") end if not data.members[member] then - return err_msg(player, "User "..member.." is not in this town.") + return err_msg(player, ("User %s is not in this town."):format(member)) end - if member == data.mayor then + if member == data.flags['mayor'] then return err_msg(player, "You cannot kick the town mayor.") end @@ -303,7 +308,7 @@ function towny.delete_town(pos,player) end local data = towny.towns[t] - if data.mayor ~= player and not towny_admin then + if data.flags['mayor'] ~= player and not towny_admin then return err_msg(player, "You do not have permission to delete this town.") end @@ -337,7 +342,7 @@ function towny.delete_plot(pos,player) 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 + if (data.flags['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 @@ -370,7 +375,7 @@ function towny.create_plot(pos,player) end local data = towny.towns[t] - if data.mayor ~= player and data.members[player]['plot_create'] ~= true and not towny_admin then + if data.flags['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 @@ -412,7 +417,7 @@ function towny.claim_plot(pos,player) local tdata = towny.towns[t] if p ~= nil then local plot_data = tdata.plots[p] - if plot_data.flags['claimable'] or player == tdata.mayor then + if plot_data.flags['claimable'] or player == tdata.flags['mayor'] then if plot_data.owner == player or plot_data.members[player] then return err_msg(player, "You are already a member of this plot.") end @@ -512,7 +517,7 @@ function towny.plot_member(pos,player,member,action) local tdata = towny.towns[t] local pdata = tdata.plots[p] - if pdata.owner ~= player and player ~= tdata.mayor and not towny_admin then + if pdata.owner ~= player and player ~= tdata.flags['mayor'] and not towny_admin then return err_msg(player, "You do not have permission to modify this plot.") end @@ -577,7 +582,7 @@ function towny.set_plot_flags(pos,player,flag,value) 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 + if data.flags['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 @@ -614,7 +619,7 @@ function towny.set_plot_member_flags(pos,player,member,flag,value) 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 + if data.flags['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 @@ -650,16 +655,22 @@ function towny.set_town_flags(pos,player,flag,value) 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 + local data = towny.towns[t] + local mayor = data.flags['mayor'] + if mayor ~= player and not towny_admin then return err_msg(player, "You do not have permission to modify this town.") end - local fs,flag,res = flag_validity(flag, 'town', value, pos) + local fs,flag,res = flag_validity(flag, 'town', value, pos, data.members) if not fs then return err_msg(player, "Invalid flag or invalid or unchangeable flag value.") end + -- Announce mayor change to all + if flag == "mayor" and res ~= mayor then + towny.chat.announce_to_members(town, ("The town mayor rights have been given to %s!"):format(res)) + end + minetest.chat_send_player(player, ("Successfully set the town flag '%s' to '%s'!"):format(flag,value)) data.flags[flag] = res towny.mark_dirty(t, false) @@ -683,7 +694,7 @@ function towny.set_town_member_flags(pos,player,member,flag,value) end local data = towny.towns[t] - if data.mayor ~= player and not towny_admin then + if data.flags['mayor'] ~= player and not towny_admin then return err_msg(player, "You do not have permission to modify this town.") end @@ -747,8 +758,8 @@ end -- Get available claim blocks function towny.get_claims_available(town) - local used = towny.get_claims_used(town) - local max = towny.get_claims_max(town) + local used = towny.get_claims_used(town) + local max = towny.get_claims_max(town) return max - used end diff --git a/visualize.lua b/visualize.lua index 4eb2c62..96d04c1 100644 --- a/visualize.lua +++ b/visualize.lua @@ -2,7 +2,7 @@ -- TODO: Use particles local r1 = towny.regions.size -local r2 = towny.regions.height + 1 +local r2 = towny.regions.height local c_obj_props = { hp = 1, glow = 1, @@ -30,11 +30,11 @@ minetest.register_entity("towny:region_visual", { end }) -function towny.regions.visualize_radius(pos) - local e = minetest.add_entity(pos, "towny:region_visual") +local function fl(x) + return math.floor(x) end function towny.regions.visualize_area(p1,p2) - local center = {x=p2.x + r1/2,y=p2.y + r2/2,z=p2.z + r1/2} + local center = {x=fl(p2.x + r1/2)+0.5,y=fl(p2.y + r2/2)+0.5,z=fl(p2.z + r1/2)+0.5} local e = minetest.add_entity(center, "towny:region_visual") end