Wuzzy c72d0ecba9 Consistent writing style of item descriptions
- Fix capitalization
- Fix ungrammatical word order (like “Wood dark”)
2019-04-13 23:52:11 -07:00

704 lines
17 KiB
Lua

--[[
ITB (insidethebox) minetest game - Copyright (C) 2017-2018 sofar & nore
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1
of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
]]--
signs = {}
local function clean_sign_entities(pos)
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0.5)) do
if not obj:is_player() and obj:get_luaentity().name == "signs:sign" then
obj:remove()
end
end
end
local function format_time(seconds)
local r = ""
local s = seconds
if s > 3600 then
local h = s / 3600
s = s % 3600
r = r .. tostring(math.floor(h)) .. "h"
end
if s > 60 then
local m = s / 60
s = s % 60
r = r .. tostring(math.floor(m)) .. "m"
end
if s > 0 then
r = r .. tostring(s) .. "s"
end
return r
end
local function sign_refresh(pos)
if not ranks or not ranks.player then
minetest.log("error", "ranks mod not found, can't update lobby signs")
return
end
clean_sign_entities(pos)
local meta = minetest.get_meta(pos)
local text = meta:get_string("text")
local ntext, count = string.gsub(text, "%$%((.-)%)", function(s)
-- highscore player 1 through 10
if s == "player1" then
return ranks.player[1]
elseif s == "player2" then
return ranks.player[2]
elseif s == "player3" then
return ranks.player[3]
elseif s == "player4" then
return ranks.player[4]
elseif s == "player5" then
return ranks.player[5]
elseif s == "player6" then
return ranks.player[6]
elseif s == "player7" then
return ranks.player[7]
elseif s == "player8" then
return ranks.player[8]
elseif s == "player9" then
return ranks.player[9]
elseif s == "player10" then
return ranks.player[10]
elseif s == "box1" then
return ranks.box[1]
elseif s == "box2" then
return ranks.box[2]
elseif s == "box3" then
return ranks.box[3]
elseif s == "box4" then
return ranks.box[4]
elseif s == "box5" then
return ranks.box[5]
elseif s == "box6" then
return ranks.box[6]
elseif s == "box7" then
return ranks.box[7]
elseif s == "box8" then
return ranks.box[8]
elseif s == "box9" then
return ranks.box[9]
elseif s == "box10" then
return ranks.box[10]
elseif s == "builder1" then
return ranks.builder[1]
elseif s == "builder2" then
return ranks.builder[2]
elseif s == "builder3" then
return ranks.builder[3]
elseif s == "builder4" then
return ranks.builder[4]
elseif s == "builder5" then
return ranks.builder[5]
elseif s == "builder6" then
return ranks.builder[6]
elseif s == "builder7" then
return ranks.builder[7]
elseif s == "builder8" then
return ranks.builder[8]
elseif s == "builder9" then
return ranks.builder[9]
elseif s == "builder10" then
return ranks.builder[10]
end
return "[" .. s .. " ???]"
end)
meta:set_string("dtext", ntext)
meta:mark_as_private("dtext")
if meta:get_string("text") ~= "" then
minetest.add_entity(pos, "signs:sign")
end
return (count > 0)
end
local function text_callback(player, fields, context)
local name = player:get_player_name()
--validate user was allowed to edit signs
if not boxes.can_edit(player) then
log.fs_data(player, name, "signs:text", fields)
return true
end
if not fields.text then
if fields.quit then
return true
end
fields.text = ""
end
-- validate length of sign text does not exceed max
if string.len(fields.text) > 1000 then
fields.text = fields.text:sub(1, 1000)
end
-- check: validate composed texture string does not exceed max
-- max 1000 characters, each character can take up to 17 chars to encode
-- in tex. So the total size is 17000 + "[combine", which easily fits
-- 64k max texture string.
--validate sign pos is actually within the box that player is in
local pos = context
if not boxes.validate_pos(player, pos) then
return true
end
-- verify node is still a sign
local node = minetest.get_node(pos)
if node.name ~= "signs:sign" and node.name ~= "signs:sign_wall" then
-- sign no longer exists. Could be lag.
return true
end
-- erase sign
clean_sign_entities(pos)
local meta = minetest.get_meta(pos)
meta:set_string("text", fields.text)
meta:mark_as_private("text")
-- for lobby signs that need a refresh
if minetest.get_node_timer(pos):is_started() then
sign_refresh(pos)
end
if fields.text ~= "" then
minetest.add_entity(pos, "signs:sign")
end
return true
end
minetest.register_node("signs:sign", {
description = "Sign",
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
inventory_image = "sign_text.png",
wield_image = "sign_text.png",
walkable = false,
tiles = {"itb_blank.png"},
groups = { node = 1, sign = 1 },
node_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2}
}
},
selection_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
colision_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
-- is player playing in this box?
local name = clicker:get_player_name()
if not boxes.can_edit(clicker) then
return itemstack
end
-- show text input form
local text = minetest.get_meta(pos):get_string("text")
fsc.show(name,
"size[8,8]" ..
"textarea[0.5,0.5;7.5,6.5;text;text;" ..
minetest.formspec_escape(text) .. "]" ..
"button_exit[3.5,7;1,0.5;exit;exit]",
pos,
text_callback)
end,
on_destruct = function(pos)
clean_sign_entities(pos)
end,
on_construct = function(pos)
minetest.get_node_timer(pos):start(300)
end,
on_timer = function(pos)
return sign_refresh(pos)
end,
after_box_construct = function(pos, box_id, player, moredata)
clean_sign_entities(pos)
local meta = minetest.get_meta(pos)
if player then
-- this is reached in exit lobbies, elswhere player = nil (mech creation of sign)
local name = player:get_player_name()
if boxes.players_in_boxes[name] then
local box = boxes.players_in_boxes[name].box_id
local bmeta = db.box_get_meta(box).meta
local text = meta:get_string("text")
local ntext = string.gsub(text, "%$%((.-)%)", function(s)
if s == "box_name" then
return bmeta.box_name
elseif s == "builder" then
return bmeta.builder
elseif s == "build_time" then
return format_time(bmeta.build_time)
elseif moredata and moredata[s] then
if moredata[s].format == "time" then
return format_time(moredata[s].data)
else
return moredata[s].data
end
end
return "[" .. s .. " ???]"
end)
meta:set_string("dtext", ntext)
meta:mark_as_private("dtext")
end
end
if meta:get_string("text") ~= "" then
minetest.add_entity(pos, "signs:sign")
end
end,
on_exit_update = function(pos, player, moredata)
minetest.registered_nodes["signs:sign"].after_box_construct(pos, nil, player, moredata)
end,
})
minetest.register_node("signs:sign_wall", {
description = "Wall sign",
drawtype = "mesh",
mesh = "signs_wall.obj",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
inventory_image = "sign.png",
walkable = false,
tiles = {"sign.png"},
groups = { node = 1, sign = 1 },
selection_box = {
type = "fixed",
fixed = {
{-1/2, -7/16, 6/16, 1/2, 7/16, 1/2},
}
},
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
-- is player playing in this box?
local name = clicker:get_player_name()
if not boxes.can_edit(clicker) then
return itemstack
end
-- show text input form
local text = minetest.get_meta(pos):get_string("text")
fsc.show(name,
"size[8,8]" ..
"textarea[0.5,0.5;7.5,6.5;text;text;" ..
minetest.formspec_escape(text) .. "]" ..
"button_exit[3.5,7;1,0.5;exit;exit]",
pos,
text_callback)
end,
on_destruct = function(pos)
clean_sign_entities(pos)
end,
after_box_construct = function(pos, box_id, player, moredata)
clean_sign_entities(pos)
local meta = minetest.get_meta(pos)
if meta:get_string("text") ~= "" then
minetest.add_entity(pos, "signs:sign")
end
end,
on_exit_update = function(pos, player, moredata)
minetest.registered_nodes["signs:sign_wall"].after_box_construct(pos, nil, player, moredata)
end,
})
local function make_tex(pos)
local meta = minetest.get_meta(pos)
local text = meta:get_string("dtext")
if text == "" then
text = meta:get_string("text")
end
-- Escape sequence for e with diaeresis mark
text = string.gsub(text, "\\\"e", string.char(0xeb))
local ww, rest = string.match(text, "^@wordwrap ([^\n]+)\n(.+)$")
local center = false
local wwwidth
if ww then
text = rest
local wr = string.match(ww, "^center (.+)$")
if wr then
center = true
ww = wr
end
wwwidth = tonumber(ww)
if wwwidth and wwwidth < 1 then
wwwidth = 1
end
end
-- count lines
local _, count = string.gsub(text, string.char(10), "")
count = count + 1
while true do
local xlen = 0
local max_width = 1000
local ok = true
if wwwidth then
max_width = wwwidth * count
end
local index = 1
local lines = {}
local idx = 1
text:gsub("()\n", function(ix)
lines[index] = text:sub(idx, ix - 1)
index = index + 1
idx = ix + 1
end)
lines[index] = text:sub(idx, string.len(text))
local split_lines = {}
if wwwidth then
local sindex = 1
for _, s in ipairs(lines) do
split_lines[sindex] = ""
local ix = 1
local column = 0
s:gsub("()([^ ]+)()", function(i1, word, i2)
local sp = i1 - ix
column = column + sp
ix = i2
if column > 0 and column + string.len(word) > max_width then
column = string.len(word)
sindex = sindex + 1
split_lines[sindex] = word
else
column = column + string.len(word)
split_lines[sindex] = split_lines[sindex] .. string.rep(" ", sp) .. word
end
if column > max_width then
ok = false
end
end)
sindex = sindex + 1
end
else
split_lines = lines
end
if count > 20 or (ok and #split_lines <= count) then
local tex = ""
local height = 8
local stride = height
for _, s in ipairs(split_lines) do
xlen = math.max(xlen, string.len(s))
end
-- compose text
for line, s in ipairs(split_lines) do
local xoff = 0
if center then
xoff = math.floor((stride * (xlen - string.len(s))) / 2)
end
for column = 1, string.len(s) do
local xpos = xoff + stride * (column - 1)
local ypos = height * (line - 1)
tex = tex .. string.format(":%d,%d=%x.png", xpos, ypos, string.byte(s, column))
end
end
if tex == "" then
tex = string.format(":%d,%d=%x.png", 0, 0, string.byte(" "))
end
local node = minetest.get_node(pos)
local visual_size
if node.name == "signs:sign_wall" then
if xlen / count < 1.0 then
visual_size = {x = 10/16 * (xlen / count), y = 10/16}
else
visual_size = {x = 10/16, y = 10/16 / (xlen / count)}
end
else
visual_size = {x = xlen / count, y = 1}
end
return "[combine:" .. (xlen * stride) .. "x" .. (height * count) .. tex, visual_size
end
count = count + 1
end
end
minetest.register_lbm({
nodenames = {"signs:sign", "signs:sign_wall"},
run_at_every_load = true,
name = "signs:load",
action = function(pos, node)
clean_sign_entities(pos)
local meta = minetest.get_meta(pos)
if meta:get_string("text") ~= "" then
minetest.add_entity(pos, "signs:sign")
end
end,
})
function signs.set_text(pos, text)
clean_sign_entities(pos)
local meta = minetest.get_meta(pos)
meta:set_string("text", text)
meta:mark_as_private("text")
if text ~= "" then
minetest.add_entity(pos, "signs:sign")
end
end
minetest.register_entity("signs:sign", {
visual = "upright_sprite",
textures = {},
collisionbox = {0, 0, 0, 0, 0, 0},
on_activate = function(self)
local pos = self.object:getpos()
if vector.distance(pos, vector.round(pos)) > 0.25 then
self.object:remove()
return
end
local textures, visual_size = make_tex(pos)
local node = minetest.get_node(pos)
local dir = minetest.facedir_to_dir(node.param2)
local yaw = minetest.dir_to_yaw(dir)
local offset
if node.name == "signs:sign_wall" then
offset = vector.multiply(dir, 1/2 - 1/16 - 1/64)
else
offset = vector.multiply(dir, 1/2 - 1/64)
end
self.object:setpos(vector.add(pos, offset))
self.object:set_properties({
textures = {textures},
visual_size = visual_size
})
self.object:set_yaw(yaw)
end,
get_staticdata = function(self)
return ""
end,
})
minetest.register_node("signs:bg", {
description = "Sign background",
tiles = {"sign_bg.png"},
groups = { node = 1 },
})
local icons = {
[1] = "nil",
[2] = "asleep",
[3] = "discontent",
[4] = "down",
[5] = "exclamation",
[6] = "heart",
[7] = "home",
[8] = "oh",
[9] = "question",
[10] = "racecar",
[11] = "raincloud",
[12] = "shades",
[13] = "smile",
[14] = "snail",
[15] = "sunny",
[16] = "up",
[17] = "weird",
[18] = "winksmile",
[19] = "xmouth",
}
local function icon_callback(player, fields, context)
if not fields.quit then
local k, _ = next(fields)
local i = tonumber(k) or 0
if not icons[i] then
return true
end
local pos = context.pos
local node = minetest.get_node(pos)
node.name = "signs:icon_" .. icons[i]
minetest.swap_node(pos, node)
end
return true
end
for k, v in ipairs(icons) do
minetest.register_node("signs:icon_".. v, {
description = "Sign (" .. v .. ")",
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
tiles = {"signs_" .. v .. ".png"},
wield_image = "signs_" .. v .. ".png",
inventory_image = "signs_" .. v .. ".png",
node_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2}
}
},
selection_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
colision_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
on_trigger = function(pos)
local box = boxes.find_box(pos)
if not box then
return
end
local name = box.name -- player name
local form = "size[9,8]\n"
for kk, vv in ipairs(icons) do
form = form ..
"image_button[" .. 1.5 * ((kk-1) % 5) + 0.7 .. "," ..
2.0 * math.floor((kk-1)/5) + 0.3 .. ";" ..
"1.35,1.35;signs_" .. vv ..
".png;" .. kk .. ";]"
if vv == "nil" then
vv = "cancel"
end
form = form ..
"label[" .. 1.5 * ((kk-1) % 5) + 0.8 .. "," ..
2.0 * math.floor((kk-1)/5) + 1.5 ..
";" .. vv .. "]\n"
end
fsc.show(name, form, {pos = pos}, icon_callback)
end,
groups = {icon = 1}, -- find icons using group:icon
icon_name = v,
icon_id = k, -- ID, must never get renumbered!
})
end
local stardir = {
[0] = {x = 1, y = 0, z = 0},
[1] = {x = 0, y = 0, z = -1},
[2] = {x = -1, y = 0, z = 0},
[3] = {x = 0, y = 0, z = 1},
}
minetest.register_node("signs:star", {
description = "Star",
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
walkable = false,
tiles = {"signs_star.png"},
inventory_image = "signs_star.png",
wield_image = "signs_star.png",
node_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2}
}
},
selection_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
colision_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
on_trigger = function(pos)
local node = minetest.get_node(pos)
local dir = stardir[node.param2]
pos = vector.add(pos, dir)
node = minetest.get_node(pos)
while node.name == "signs:star" do
-- make this a no_star
node.name = "signs:no_star"
minetest.swap_node(pos, node)
-- iterate right
pos = vector.add(pos, dir)
node = minetest.get_node(pos)
end
end,
groups = {star = 1}, -- count stars by finding them using group:star
})
minetest.register_node("signs:no_star", {
description = "No star",
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
walkable = false,
tiles = {"signs_nil.png"},
inventory_image = "signs_no_star_inv.png",
wield_image = "signs_nil.png",
groups = { node = 1 },
node_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2}
}
},
selection_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
colision_box = {
type = "fixed",
fixed = {
{-1/2, -1/2, 7/16, 1/2, 1/2, 1/2},
}
},
on_trigger = function(pos)
local node = minetest.get_node(pos)
local dir = stardir[node.param2]
while node.name == "signs:no_star" do
-- make this a no_star
node.name = "signs:star"
minetest.swap_node(pos, node)
-- iterate left
pos = vector.subtract(pos, dir)
node = minetest.get_node(pos)
end
end,
})