Nations, chat modification and HUD

This commit is contained in:
Evert Prants 2019-01-16 16:41:04 +02:00
parent 6b5beed9ee
commit a58783bf67
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
11 changed files with 574 additions and 17 deletions

View File

@ -28,10 +28,14 @@ end
local function invite_player(town,player,target)
local utown = towny.get_player_town(player)
if not utown or utown ~= town then
if not utown then
return false, "You are not in a town."
end
if target == player then
return false, "You cannot invite yourself!"
end
if not minetest.get_player_by_name(target) then
return false, "You can only invite online players to your town."
end
@ -67,7 +71,7 @@ end
local function invite_respond(player,response)
local utown = towny.get_player_town(player)
if utown or utown ~= town then
if utown then
return false, "You are already in a town."
end
@ -88,7 +92,7 @@ local function invite_respond(player,response)
return false, "You do not have any pending invites."
end
local function send_flags (flags,message)
function towny.chat.send_flags (flags,message)
local shiny = {}
for flag,value in pairs(flags) do
if type(value) == "table" then
@ -121,7 +125,7 @@ local function town_command (name, param)
if (pr1 == "create" or pr1 == "new") and pr2 then
return towny.create_town(nil, name, pr2)
elseif (pr1 == "invite" and not minetest.get_player_by_name(pr2)) then
return invite_respond(name, (tyes:lower() == "accept" or minetest.is_yes(tyes)))
return invite_respond(name, (pr2:lower() == "accept" or minetest.is_yes(pr2)))
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
@ -162,7 +166,7 @@ local function town_command (name, param)
elseif param == "flags" then
local flags = towny.get_flags(town)
if flags then
return send_flags(flags,"Flags of your town")
return towny.chat.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
@ -208,7 +212,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(flags,"Flags of this plot")
return towny.chat.send_flags(flags,"Flags of this plot")
else
return false, "There's no plot here."
end

View File

@ -83,7 +83,7 @@ towny = {
['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"},
['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},
@ -96,11 +96,11 @@ towny = {
['plot_delete'] = {"boolean", "member can delete plots"},
},
plot = {
['teleport'] = {"vector", "plot's teleport point"},
['pvp'] = {"boolean", "players can fight here if true, ignores server pvp settings"},
['cost'] = {"number", "plot cost (only with economy)"},
['teleport'] = {"vector", "plot's teleport point"},
['pvp'] = {"boolean", "players can fight here if true, ignores server pvp settings"},
['cost'] = {"number", "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\")"},
['greeting'] = {"string", "plot's greeting message (defaults to \"{owner}'s Plot\"/\"Unclaimed Plot\")"},
},
plot_member = {
['plot_build'] = {"boolean", "member can build on plot. defaults to 'plot_member_build' town flag"},

View File

@ -68,7 +68,17 @@ function towny.storage.save_town_meta(town)
end
end
local ldirs = { "meta", "region" }
function towny.storage.save_nation_meta(nation)
local nmeta = towny.nations.nations[nation]
if nmeta and nmeta.dirty then
minetest.after(0.2, function ()
write_meta(town,"nation",nmeta)
nmeta.dirty = false
end)
end
end
local ldirs = { "meta", "region", "nation" }
function towny.storage.load_all_towns()
local world = minetest.get_worldpath()
local metadir = world.."/towny/"..ldirs[1]
@ -101,6 +111,24 @@ function towny.storage.load_all_towns()
end)
end
end
if towny.nations then
local nationdir = world.."/towny/"..ldirs[3]
minetest.mkdir(nationdir)
local nations = minetest.get_dir_list(nationdir, false)
for _,file in pairs(nations) do
if file:match("."..extension.."$") then
local nation = file:gsub("."..extension,"")
minetest.after(0.1, function ()
local nationdata = load_meta(nationdir.."/"..file)
if not nationdata then return end
towny.nations.nations[nation] = nationdata
towny.nations.get_nation_level(nation, true)
end)
end
end
end
end
function towny.storage.delete_all_meta(town)

View File

@ -22,6 +22,14 @@ local function carrier_tick()
end
end
if towny.nations then
for nation,data in pairs(towny.nations.nations) do
if data.dirty then
towny.storage.save_nation_meta(nation)
end
end
end
towny.dirty = false
saving = false
end

View File

@ -4,11 +4,13 @@
local storage = minetest.get_mod_storage()
local function write_meta(town,scope,data)
local data = table.copy(data)
data.dirty = nil
data.level = nil
local serialized = minetest.serialize(data)
storage:set_string(town.."/"..scope, serialized)
data = nil
end
function towny.storage.save_town_meta(town)
@ -26,6 +28,15 @@ function towny.storage.save_town_meta(town)
end
end
function towny.storage.save_nation_meta(nation)
if not towny.nations then return end
local rmeta = towny.nations.nations[nation]
if rmeta and rmeta.dirty then
write_meta(nation,"nation",rmeta)
rmeta.dirty = false
end
end
-- Ideally only ever called once
function towny.storage.load_all_towns()
local keys = {}
@ -44,6 +55,9 @@ function towny.storage.load_all_towns()
towny.get_town_level(town, true)
elseif scope == "region" then
towny.regions.memloaded[town] = tbl
elseif scope == "nation" and towny.nations then
towny.nations.nations[town] = tbl
towny.nations.get_nation_level(town, true)
end
end
end
@ -57,4 +71,8 @@ function towny.storage.delete_all_meta(town)
if storage:get_string(town.."/region") ~= "" then
storage:set_string(town.."/region", "")
end
if storage:get_string(town.."/nation") ~= "" then
storage:set_string(town.."/nation", "")
end
end

View File

@ -31,7 +31,7 @@ local function flag_typeify(value,pos)
return value
end
local function flag_validity(flag,scope,value,pos,members)
function towny.flag_validity(flag,scope,value,pos,members)
value = flag_typeify(value,pos)
local spd = towny.flags[scope]
if type(spd[flag]) == "string" then
@ -586,7 +586,7 @@ function towny.set_plot_flags(pos,player,flag,value)
return err_msg(player, "You do not have permission to modify this plot.")
end
local fs,flag,res = flag_validity(flag, 'plot', value, pos)
local fs,flag,res = towny.flag_validity(flag, 'plot', value, pos)
if not fs then
return err_msg(player, "Invalid flag or flag value.")
end
@ -627,7 +627,7 @@ function towny.set_plot_member_flags(pos,player,member,flag,value)
return err_msg(player, "There is no such member in this plot.")
end
local fs,flag,res = flag_validity(flag, 'plot_member', value, pos)
local fs,flag,res = towny.flag_validity(flag, 'plot_member', value, pos)
if not fs then
return err_msg(player, "Invalid flag or flag value.")
end
@ -661,7 +661,7 @@ function towny.set_town_flags(pos,player,flag,value)
return err_msg(player, "You do not have permission to modify this town.")
end
local fs,flag,res = flag_validity(flag, 'town', value, pos, data.members)
local fs,flag,res = towny.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
@ -702,7 +702,7 @@ function towny.set_town_member_flags(pos,player,member,flag,value)
return err_msg(player, "There is no such member in this town.")
end
local fs,flag,res = flag_validity(flag, 'town_member', value, pos)
local fs,flag,res = towny.flag_validity(flag, 'town_member', value, pos)
if not fs then
return err_msg(player, "Invalid flag or flag value.")
end
@ -750,9 +750,19 @@ function towny.get_claims_max(town)
if not tdata then return 0 end
if not tdata.level then towny.get_town_level(town, true) end
local bonus = 0
if tdata.flags['claim_blocks'] and tdata.flags['claim_blocks'] > 0 then
bonus = tdata.flags['claim_blocks']
end
if towny.nations then
local n = towny.nations.get_town_nation(town)
local ndata = towny.nations.nations[n]
if n and ndata and ndata.level then
bonus = bonus + ndata.level.block_bonus
end
end
return tdata.level.claimblocks + bonus, tdata.level.claimblocks, bonus
end
@ -776,6 +786,23 @@ function towny.get_full_name(town)
return ("%s (%s)"):format(tdata.name, tdata.level.name_tag)
end
function towny.get_player_name(player)
local town = towny.get_player_town(player)
local tdata = towny.towns[town]
if not tdata then return player end
if not tdata.level then return player end
if towny.nations and tdata.flags.mayor == player then
local n = towny.nations.get_town_nation(town)
if n then
local name = towny.nations.get_player_name(n,player)
if name then
return name
end
end
end
return ("%s %s"):format(tdata.level.mayor_tag, player)
end
function towny.get_town_level(town, update)
local tdata = towny.towns[town]
if not tdata then return nil end

View File

@ -3,3 +3,13 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
towny.chat.modpath = modpath
minetest.register_on_chat_message(function (name, message)
local town = towny.get_player_town(name)
local result = ("<%s> %s"):format(name, message)
if town then
result = ("[%s] <%s> %s"):format(towny.get_full_name(town), towny.get_player_name(name), message)
end
minetest.chat_send_all(result)
return true
end)

View File

@ -2,3 +2,83 @@
-- The MIT License - 2019 Evert "Diamond" Prants <evert@lunasqu.ee>
local modpath = minetest.get_modpath(minetest.get_current_modname())
-- This code is borrowed from [areas] by ShadowNinja
-- TODO: Support areas (areas:getExternalHudEntries)
towny.hud = {}
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local pos = vector.round(player:getpos())
local areaStrings = {}
local t,p,c = towny.regions.get_town_at(pos)
if t then
if towny.nations then
local n = towny.nations.get_town_nation(t)
if n then
table.insert(areaStrings, towny.nations.get_full_name(n))
end
end
local town = towny.get_full_name(t)
local tdata = towny.towns[t]
local greeting = ""
if tdata.flags.greeting then
greeting = "- "..tdata.flags.greeting
end
table.insert(areaStrings, ("%s %s"):format(town, greeting))
if p then
local plot = tdata.plots[p]
local greeting = ("%s's Plot"):format(plot.owner)
if plot.flags['greeting'] then
greeting = ("%s (%s)"):format(plot.flags['greeting'], plot.owner)
end
if plot.flags['claimable'] then
if plot.flags['cost'] and plot.flags['cost'] > 0 then
-- TODO: economy
end
greeting = greeting .. " (For sale: FREE!)"
end
table.insert(areaStrings, ("%s - %s"):format(town, greeting))
end
end
local areaString = "Towns:"
if #areaStrings > 0 then
areaString = areaString.."\n"..
table.concat(areaStrings, "\n")
end
local hud = towny.hud[name]
if not hud then
hud = {}
towny.hud[name] = hud
hud.areasId = player:hud_add({
hud_elem_type = "text",
name = "Towns",
number = 0xFFFFFF,
position = {x=0, y=1},
offset = {x=8, y=-8},
text = areaString,
scale = {x=200, y=60},
alignment = {x=1, y=-1},
})
hud.oldAreas = areaString
return
elseif hud.oldAreas ~= areaString then
player:hud_change(hud.areasId, "text", areaString)
hud.oldAreas = areaString
end
end
end)
minetest.register_on_leaveplayer(function(player)
towny.hud[player:get_player_name()] = nil
end)

176
towny_nations/commands.lua Normal file
View File

@ -0,0 +1,176 @@
towny.chat.invites.nation = {}
-- Send message to all town members who are online
function towny.nations.announce_to_members(nation,message)
local ndata = towny.nations.nations[nation]
if ndata then return end
for town in pairs(ndata.members) do
towny.chat.announce_to_members(town,message)
end
end
local function join_nation(nation,player,from_invite)
local town = towny.get_player_town(player)
local tdata = towny.towns[town]
local ndata = towny.nations.nations[nation]
if not ndata then return false, "The nation specified does not exist." end
if not tdata then return false, "You are not in a town that could join a nation." end
if towny.nations.get_town_nation(town) then return false, "Your town is already part of a nation." end
if tdata.flags.mayor ~= player then return false, "Only the mayor can join their town into a nation." end
if (not from_invite and not ndata.flags['joinable']) then return false, "You cannot join this nation." end
towny.nations.announce_to_members(town, minetest.colorize("#02aacc", ("%s has joined the nation!"):format(towny.get_full_name(town))))
minetest.chat_send_player(player, ("Your town has successfully joined %s!"):format(towny.nations.get_full_name(nation)))
ndata.members[town] = {}
ndata.dirty = true
towny.dirty = true
return true
end
local function invite_respond(player,response)
local utown = towny.get_player_town(player)
if not utown then
return false, "You are not in a town."
end
local unation = towny.nations.get_town_nation(utown)
if unation then
return false, "Your town is already part of a nation."
end
for id,data in pairs(towny.chat.invites.nation) do
if data.player == player then
if not data.rejected then
if response == true then
towny.chat.invites.nation[id] = nil
return join_nation(data.nation,player,true)
else
towny.chat.invites.nation[id] = { rejected = true }
return true, "You have rejected the join request."
end
end
end
end
return false, "You do not have any pending invites."
end
local function invite_town(player,town)
local utown = towny.get_player_town(player)
if not utown then
return false, "You are not in a town."
end
if town == utown then
return false, "You cannot invite yourself!"
end
local nation = towny.nations.get_town_nation(utown)
if not nation then
return false, "Your town is not part of any nation."
end
local utdata = towny.towns[utown]
local ndata = towny.nations.nations[nation]
if utdata.flags.mayor ~= player or ndata.flags.capital ~= utown then
return false, "You can only invite towns to your nation if you own said nation!"
end
if not towny.get_town_by_name(town) then
return false, "Invalid town name."
end
local target_town = towny.nations.get_town_nation(town)
if target_town then
return false, "This town is already part of a nation!"
end
if towny.chat.invites.nation[town.."-"..nation] then
return false, "This town has already been invited to join your nation!"
end
local tdata = towny.towns[town]
local target = tdata.flags['mayor']
if not minetest.get_player_by_name(target) then
return false, "The mayor of the targeted town is offline, thus you cannot invite this town to your nation!"
end
minetest.chat_send_player(target, ("Your town has been invited to join %s by %s"):format(towny.nations.get_full_name(nation), player))
minetest.chat_send_player(target, "You can accept this invite by typing '/nation invite accept' or deny '/nation invite deny'")
towny.chat.invites[town.."-"..nation] = { rejected = false, nation = nation, town = town, invited = player }
return true, ("Town %s has been invited to join your nation."):format(town)
end
local function nation_command(name, param)
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)
if not town then
return false, "You are not currently in any town."
end
local nation = towny.nations.get_town_nation(town)
local tdata = towny.towns[town]
-- Pre nation requirement
local nation_info = nil
if (pr1 == "create" or pr1 == "new") and pr2 then
return towny.nations.create_nation(pr2,name)
elseif (pr1 == "invite" and not towny.get_town_by_name(pr2)) then
return invite_respond(name, (pr2:lower() == "accept" or minetest.is_yes(pr2)))
elseif pr1 == "join" and towny.nations.get_nation_by_name(pr2) and not nation then
return join_nation(pr2,name,false)
elseif pr1 == "show" or pr1 == "info" then
if not towny.get_town_by_name(pr2) then
return false, "No such nation."
end
nation_info = pr2
elseif param == "" and nation then
nation_info = nation
end
-- Print nation information
if nation_info then
return false, "Not yet implemented!"
end
if not nation then
return false, "You are not currently in a nation."
end
local ndata = towny.nations.nations[nation]
local capital = towny.towns[ndata.flags.capital]
if param == "leave" or param == "delete" then
return towny.nations.leave_nation(name)
elseif param == "teleport" and capital then
local portal = capital.flags['teleport']
if not portal then portal = capital.flags['origin'] end
player:set_pos(portal)
return true, "Teleporting you to the nation's capital town.."
elseif param == "flags" then
local flags = towny.nations.get_flags(nation)
if flags then
return towny.chat.send_flags(flags,"Flags of your nation")
end
elseif pl1 == "set" and pl2 then
local flag, value = string.match(pl2, "^([%a%d_-]+) (.+)$")
return towny.nations.set_nation_flags(name,flag,value)
elseif pr1 == "invite" and towny.get_town_by_name(pr2) then
return invite_town(name,towny.get_town_by_name(pr2))
end
return false, "Invalid command usage."
end
minetest.register_chatcommand("nation", {
description = "Manage your nation",
privs = {towny = true},
func = nation_command
})

View File

@ -4,6 +4,7 @@
local modpath = minetest.get_modpath(minetest.get_current_modname())
towny.nations = {
modpath = modpath,
nations = {},
levels = {
{
king_tag = 'Leader',
@ -44,3 +45,12 @@ towny.nations = {
}
},
}
towny.flags.nation = {
['tax'] = {"number", "how much each town has to pay each day to stay in the nation"},
['capital'] = {"member", "nation's capital town"},
['bank'] = {"number", "nation's treasury", false},
}
dofile(modpath.."/nation.lua")
dofile(modpath.."/commands.lua")

196
towny_nations/nation.lua Normal file
View File

@ -0,0 +1,196 @@
local function err_msg(player, msg)
minetest.chat_send_player(player, minetest.colorize("#ff1111", msg))
return false
end
local function count(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
local function mark_dirty(nation)
towny.dirty = true
towny.nations.nations[nation].dirty = true
end
function towny.nations.get_nation_by_name(name)
if not name then return nil end
for town,data in pairs(towny.nations.nations) do
if data.name:lower() == name:lower() then
return town
end
end
return nil
end
function towny.nations.get_town_nation(town)
if not town or not towny.towns[town] then return nil end
for tid,data in pairs(towny.nations.nations) do
if data.flags.capital == town or data.members[town] then
return tid
end
end
return nil
end
function towny.nations.create_nation(name,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 tdata = towny.towns[town]
if tdata.flags['mayor'] ~= player then
return err_msg(player, "Only the town mayor can create a nation!")
end
if towny.nations.get_town_nation(town) then
return err_msg(player, "You're already part of a nation!")
end
if towny.nations.get_nation_by_name(name) then
return err_msg(player, "A nation by that name already exists!")
end
-- TODO: economy
local vertpos = {x = math.random(-999,999), y = math.random(-999,999), z = math.random(-999,999)}
local nid = minetest.sha1(minetest.hash_node_position(vertpos))
towny.nations.nations[nid] = {
name = name,
members = {[town] = {}},
flags = {capital = town},
}
mark_dirty(nid)
minetest.chat_send_player(player, "Your nation has successfully been founded!")
minetest.chat_send_all(("%s has started a new nation called '%s'!"):format(player,name))
return true
end
function towny.nations.leave_nation(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 tdata = towny.towns[town]
if tdata.flags['mayor'] ~= player then
return err_msg(player, "Only the town mayor can leave the nation!")
end
local nation = towny.nations.get_town_nation(town)
local ndata = towny.nations.nations[nation]
if not nation or not ndata then
return err_msg(player, "Your town is currently not part of any nation!")
end
if ndata.flags['capital'] == town and count(ndata.members) > 1 then
return err_msg(player, "Your nation contains more than one member towns! Please remove the towns or change the capital before deleting your nation.")
end
if ndata.flags['capital'] == town and count(ndata.members) <= 1 then
-- Single member town, delete nation
towny.storage.delete_all_meta(nation)
towny.nations.nations[nation] = nil
minetest.chat_send_player(player, "Successfully deleted the nation!")
minetest.chat_send_all(("The nation '%s' has fallen."):format(ndata.name))
else
-- Simply leave
minetest.chat_send_player(player, "Successfully left the nation!")
towny.nations.nations[nation].members[town] = nil
towny.nations.announce_to_members(nation, ("Town '%s' has left the nation."):format(tdata.name))
mark_dirty(nation)
end
return true
end
function towny.nations.set_nation_flags(player,flag,value)
if not flag then return false 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 nation = towny.nations.get_nation_by_name(name)
local ndata = towny.nations.nations[name]
local tdata = towny.towns[town]
if not nation or not ndata then
return err_msg(player, "Your town is currently not part of any nation!")
end
local ndata = towny.nations.nations[t]
if tdata.flags.capital ~= ndata.name or tdata.flags.mayor ~= player then
return err_msg(player, "You do not have permission to modify this nation.")
end
local val_pass = value
if flag == "capital" then
val_pass = towny.get_town_by_name(value)
end
local fs,flag,res = towny.flag_validity(flag, 'nation', val_pass, nil, ndata.members)
if not fs then
return err_msg(player, "Invalid flag or invalid or unchangeable flag value.")
end
-- Announce capital change to all
if flag == "capital" and res ~= town then
towny.nations.announce_to_members(nation, ("The nation's capital has been changed to %s!"):format(towny.get_full_name(res)))
end
minetest.chat_send_player(player, ("Successfully set the nation flag '%s' to '%s'!"):format(flag,value))
ndata.flags[flag] = res
mark_dirty(nation)
end
function towny.nations.get_nation_level(nation, update)
local ndata = towny.nations.nations[nation]
if not ndata then return nil end
if ndata.level and not update then return ndata.level end
local lvl
for _,describe in pairs(towny.nations.levels) do
if count(ndata.members) >= describe.members then
lvl = describe
end
end
ndata.level = lvl
return lvl
end
function towny.nations.get_full_name(nation)
local ndata = towny.nations.nations[nation]
if not ndata then return nil end
if not ndata.level then return ndata.name end
return ("%s %s %s"):format(ndata.level.prefix, ndata.name, ndata.level.tag)
end
function towny.nations.get_player_name(nation,player)
local ndata = towny.nations.nations[nation]
if not ndata then return player end
if not ndata.level then return player end
local cap = towny.towns[ndata.flags.capital]
if not cap or not cap.members[player] then return player end
if cap.flags.mayor ~= player then return nil end
return ("%s %s"):format(ndata.level.king_tag, player)
end
function towny.nations.get_member_count(nation)
local ndata = towny.nations.nations[nation]
if not ndata then return nil end
return count(ndata.members)
end
function towny.nations.get_flags(nation)
local ndata = towny.nations.nations[nation]
if not ndata then return nil end
ndata.flags.capital = towny.towns[ndata.flags.capital].name
return ndata.flags
end