Initial commit

This commit is contained in:
Evert Prants 2019-01-12 17:49:44 +02:00
commit 585cab107b
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
10 changed files with 892 additions and 0 deletions

67
chat.lua Normal file
View File

@ -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
})

1
description.txt Normal file
View File

@ -0,0 +1 @@
A township system for Minetest servers.

0
flatfile.lua Normal file
View File

94
init.lua Normal file
View File

@ -0,0 +1,94 @@
-- A township system for Minetest servers.
-- The MIT License - 2019 Evert "Diamond" Prants <evert@lunasqu.ee>
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 = {<members with flags>},
flags = {<town specific flags>},
plots = {
plot_id = {
owner = "Owner name",
members = {<members with flags>},
flags = {<plot specific flags>}
}
}
}
]]
-- Town regions data structure
--[[
town_id = {
origin = <town 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")

2
mod.conf Normal file
View File

@ -0,0 +1,2 @@
name = towny
description = A township system for Minetest servers.

256
regions.lua Normal file
View File

@ -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]]

40
settingtypes.txt Normal file
View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

393
town.lua Normal file
View File

@ -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

39
visualize.lua Normal file
View File

@ -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