4 local chars_file
= io
.open(minetest
.get_modpath("mcl_signs").."/characters", "r")
5 -- FIXME: Support more characters (many characters are missing)
8 minetest
.log("error", "[mcl_signs] : character map file not found")
11 local char
= chars_file
:read("*l")
15 local img
= chars_file
:read("*l")
22 local SIGN_WIDTH
= 115
24 local LINE_LENGTH
= 15
25 local NUMBER_OF_LINES
= 4
27 local LINE_HEIGHT
= 14
32 local function round(num
, idp
)
33 local mult
= 10^
(idp
or 0)
34 return math
.floor(num
* mult
+ 0.5) / mult
37 local string_to_array
= function(str
)
39 for i
=1,string.len(str
) do
40 table.insert(tab
, string.sub(str
, i
,i
))
45 local string_to_line_array
= function(str
)
50 for _
,char
in ipairs(string_to_array(str
)) do
56 -- This check cuts off overlong lines
57 elseif linechar
<= LINE_LENGTH
then
58 tab
[current
] = tab
[current
]..char
59 linechar
= linechar
+ 1
65 local create_lines
= function(text
)
68 for _
, line
in ipairs(string_to_line_array(text
)) do
69 if line_num
> NUMBER_OF_LINES
then
72 table.insert(tab
, line
)
73 line_num
= line_num
+ 1
78 local generate_line
= function(s
, ypos
)
83 local printed_char_width
= CHAR_WIDTH
+ 1
84 while chars
<= LINE_LENGTH
and i
<= #s
do
86 if charmap
[s
:sub(i
, i
)] ~= nil then
87 file
= charmap
[s
:sub(i
, i
)]
89 elseif i
< #s
and charmap
[s
:sub(i
, i
+ 1)] ~= nil then
90 file
= charmap
[s
:sub(i
, i
+ 1)]
93 minetest
.log("warning", "[mcl_signs] Unknown symbol in '"..s
.."' at "..i
.." (probably "..s
:sub(i
, i
)..")")
97 width
= width
+ printed_char_width
98 table.insert(parsed
, file
)
105 local xpos
= math
.floor((SIGN_WIDTH
- width
) / 2)
106 for i
= 1, #parsed
do
107 texture
= texture
..":"..xpos
..","..ypos
.."="..parsed
[i
]..".png"
108 xpos
= xpos
+ printed_char_width
113 local generate_texture
= function(lines
, signnodename
)
114 local texture
= "[combine:"..SIGN_WIDTH
.."x"..SIGN_WIDTH
116 if signnodename
== "mcl_signs:wall_sign" then
122 texture
= texture
..generate_line(lines
[i
], ypos
)
123 ypos
= ypos
+ LINE_HEIGHT
128 local n
= 23/56 - 1/128
130 local signtext_info_wall
= {
131 {delta
= {x
= 0, y
= 0, z
= n
}, yaw
= 0},
132 {delta
= {x
= n
, y
= 0, z
= 0}, yaw
= math
.pi
/ -2},
133 {delta
= {x
= 0, y
= 0, z
= -n
}, yaw
= math
.pi
},
134 {delta
= {x
= -n
, y
= 0, z
= 0}, yaw
= math
.pi
/ 2},
137 local signtext_info_standing
= {}
139 local m
= -1/16 + 1/64
141 local yaw
= math
.pi
*2 - (((math
.pi
*2) / 16) * rot
)
142 local delta
= vector
.multiply(minetest
.yaw_to_dir(yaw
), m
)
143 -- Offset because sign is a bit above node boundaries
144 delta
.y
= delta
.y
+ 2/28
145 table.insert(signtext_info_standing
, { delta
= delta
, yaw
= yaw
})
148 local function get_rotation_level(facedir
, nodename
)
149 local rl
= facedir
* 4
150 if nodename
== "mcl_signs:standing_sign22_5" then
152 elseif nodename
== "mcl_signs:standing_sign45" then
154 elseif nodename
== "mcl_signs:standing_sign67_5" then
160 local sign_groups
= {handy
=1,axey
=1, flammable
=1, deco_block
=1, material_wood
=1, attached_node
=1}
162 local destruct_sign
= function(pos
)
163 local objects
= minetest
.get_objects_inside_radius(pos
, 0.5)
164 for _
, v
in ipairs(objects
) do
165 local ent
= v
:get_luaentity()
166 if ent
and ent
.name
== "mcl_signs:text" then
172 local update_sign
= function(pos
, fields
, sender
)
173 local meta
= minetest
.get_meta(pos
)
177 local text
= meta
:get_string("text")
178 if fields
and (text
== "" and fields
.text
) then
179 meta
:set_string("text", fields
.text
)
185 local objects
= minetest
.get_objects_inside_radius(pos
, 0.5)
186 for _
, v
in ipairs(objects
) do
187 local ent
= v
:get_luaentity()
188 if ent
and ent
.name
== "mcl_signs:text" then
189 v
:set_properties({textures
={generate_texture(create_lines(text
), v
:get_luaentity()._signnodename
)}})
194 -- if there is no entity
196 local n
= minetest
.get_node(pos
)
198 if nn
== "mcl_signs:standing_sign" or nn
== "mcl_signs:standing_sign22_5" or nn
== "mcl_signs:standing_sign45" or nn
== "mcl_signs:standing_sign67_5" then
199 sign_info
= signtext_info_standing
[get_rotation_level(n
.param2
, nn
) + 1]
200 elseif nn
== "mcl_signs:wall_sign" then
201 sign_info
= signtext_info_wall
[n
.param2
+ 1]
203 if sign_info
== nil then
206 local text_entity
= minetest
.add_entity({
207 x
= pos
.x
+ sign_info
.delta
.x
,
208 y
= pos
.y
+ sign_info
.delta
.y
,
209 z
= pos
.z
+ sign_info
.delta
.z
}, "mcl_signs:text")
210 if nn
== "mcl_signs:standing_sign22_5" then
211 sign_info
.yaw
= sign_info
.yaw
+ math
.pi
/ 8
212 elseif nn
== "mcl_signs:standing_sign45" then
213 sign_info
.yaw
= sign_info
.yaw
+ 2 * (math
.pi
/ 8)
214 elseif nn
== "mcl_signs:standing_sign67_5" then
215 sign_info
.yaw
= sign_info
.yaw
+ 3 * (math
.pi
/ 8)
217 text_entity
:get_luaentity()._signnodename
= nn
219 text_entity
:setyaw(sign_info
.yaw
)
222 local show_formspec
= function(player
, pos
)
223 minetest
.show_formspec(
224 player
:get_player_name(),
225 "mcl_signs:set_text_"..pos
.x
.."_"..pos
.y
.."_"..pos
.z
,
226 "size[6,3]textarea[0.25,0.25;6,1.5;text;Edit sign text:;]label[0,1.5;Maximum line length: 15\nMaximum lines: 4]button_exit[0,2.5;6,1;submit;Done]"
230 minetest
.register_on_player_receive_fields(function(player
, formname
, fields
)
231 if formname
:find("mcl_signs:set_text_") == 1 then
232 local x
, y
, z
= formname
:match("mcl_signs:set_text_(.-)_(.-)_(.*)")
233 local pos
= {x
=tonumber(x
), y
=tonumber(y
), z
=tonumber(z
)}
234 if not pos
or not pos
.x
or not pos
.y
or not pos
.z
then return end
235 update_sign(pos
, fields
, player
)
240 if minetest
.get_modpath("mcl_sounds") then
241 node_sounds
= mcl_sounds
.node_sound_wood_defaults()
244 minetest
.register_node("mcl_signs:wall_sign", {
245 description
= "Sign",
246 _doc_items_longdesc
= "Signs can be written and come in two variants: Wall sign and sign on a sign post. Signs can be placed on the top and the sides of other blocks, but not below them.",
247 _doc_items_usagehelp
= "Place the sign at the side to build a wall sign, place it on top of another block to build a sign with a sign post.\nAfter placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. The text can not be changed once it has been written; you have to break and place the sign again.",
248 inventory_image
= "default_sign.png",
250 is_ground_content
= false,
251 wield_image
= "default_sign.png",
252 node_placement_prediction
= "",
254 sunlight_propagates
= true,
255 paramtype2
= "wallmounted",
257 mesh
= "mcl_signs_signonwallmount.obj",
258 selection_box
= {type = "wallmounted", wall_side
= {-0.5, -7/28, -0.5, -23/56, 7/28, 0.5}},
259 tiles
= {"mcl_signs_sign.png"},
260 groups
= sign_groups
,
262 sounds
= node_sounds
,
264 on_place
= function(itemstack
, placer
, pointed_thing
)
265 local above
= pointed_thing
.above
266 local under
= pointed_thing
.under
268 -- Use pointed node's on_rightclick function first, if present
269 local node_under
= minetest
.get_node(under
)
270 if placer
and not placer
:get_player_control().sneak
then
271 if minetest
.registered_nodes
[node_under
.name
] and minetest
.registered_nodes
[node_under
.name
].on_rightclick
then
272 return minetest
.registered_nodes
[node_under
.name
].on_rightclick(under
, node_under
, placer
, itemstack
) or itemstack
276 local dir
= vector
.subtract(under
, above
)
278 -- Only build when it's legal
279 local abovenodedef
= minetest
.registered_nodes
[minetest
.get_node(above
).name
]
280 if not abovenodedef
or abovenodedef
.buildable_to
== false then
284 local wdir
= minetest
.dir_to_wallmounted(dir
)
286 local placer_pos
= placer
:getpos()
288 local fdir
= minetest
.dir_to_facedir(dir
)
291 local nodeitem
= ItemStack(itemstack
)
294 --how would you add sign to ceiling?
297 elseif wdir
== 1 then
300 -- Determine the sign rotation based on player's yaw
301 local yaw
= math
.pi
*2 - placer
:get_look_horizontal()
303 -- Select one of 16 possible rotations (0-15)
304 local rotation_level
= round((yaw
/ (math
.pi
*2)) * 16)
306 if rotation_level
> 15 then
308 elseif rotation_level
< 0 then
312 -- The actual rotation is a combination of predefined mesh and facedir (see node definition)
313 if rotation_level
% 4 == 0 then
314 nodeitem
:set_name("mcl_signs:standing_sign")
315 elseif rotation_level
% 4 == 1 then
316 nodeitem
:set_name("mcl_signs:standing_sign22_5")
317 elseif rotation_level
% 4 == 2 then
318 nodeitem
:set_name("mcl_signs:standing_sign45")
319 elseif rotation_level
% 4 == 3 then
320 nodeitem
:set_name("mcl_signs:standing_sign67_5")
322 fdir
= math
.floor(rotation_level
/ 4)
325 local _
, success
= minetest
.item_place_node(nodeitem
, placer
, pointed_thing
, fdir
)
329 if not minetest
.settings
:get_bool("creative_mode") then
330 itemstack
:take_item()
332 sign_info
= signtext_info_standing
[rotation_level
+ 1]
336 local _
, success
= minetest
.item_place_node(itemstack
, placer
, pointed_thing
, wdir
)
340 sign_info
= signtext_info_wall
[fdir
+ 1]
343 -- Determine spawn position of entity
345 if minetest
.registered_nodes
[node_under
.name
].buildable_to
then
351 local text_entity
= minetest
.add_entity({
352 x
= place_pos
.x
+ sign_info
.delta
.x
,
353 y
= place_pos
.y
+ sign_info
.delta
.y
,
354 z
= place_pos
.z
+ sign_info
.delta
.z
}, "mcl_signs:text")
355 text_entity
:setyaw(sign_info
.yaw
)
356 text_entity
:get_luaentity()._signnodename
= nodeitem
:get_name()
358 minetest
.sound_play({name
="default_place_node_hard", gain
=1.0}, {pos
= place_pos
})
360 show_formspec(placer
, place_pos
)
363 on_destruct
= destruct_sign
,
364 on_receive_fields
= function(pos
, formname
, fields
, sender
)
365 update_sign(pos
, fields
, sender
)
367 on_punch
= function(pos
, node
, puncher
)
371 _mcl_blast_resistance
= 5,
375 if minetest
.get_modpath("screwdriver") then
376 on_rotate
= screwdriver
.disallow
379 -- Standing sign nodes.
380 -- 4 rotations at 0°, 22.5°, 45° and 67.5°.
381 -- These are 4 out of 16 possible rotations.
382 -- With facedir the remaining 12 rotations are constructed.
387 sunlight_propagates
= true,
389 is_ground_content
= false,
390 paramtype2
= "facedir",
392 mesh
= "mcl_signs_sign.obj",
393 selection_box
= {type = "fixed", fixed
= {-0.2, -0.5, -0.2, 0.2, 0.5, 0.2}},
394 tiles
= {"mcl_signs_sign.png"},
395 groups
= sign_groups
,
396 drop
= "mcl_signs:wall_sign",
398 sounds
= node_sounds
,
400 on_destruct
= destruct_sign
,
401 on_receive_fields
= function(pos
, formname
, fields
, sender
)
402 update_sign(pos
, fields
, sender
)
404 on_punch
= function(pos
, node
, puncher
)
408 on_rotate
= on_rotate
,
410 _mcl_blast_resistance
= 5,
414 minetest
.register_node("mcl_signs:standing_sign", ssign
)
415 local ssign22_5
= table.copy(ssign
)
416 ssign22_5
.mesh
= "mcl_signs_sign22.5.obj"
419 minetest
.register_node("mcl_signs:standing_sign22_5", ssign22_5
)
420 local ssign45
= table.copy(ssign
)
421 ssign45
.mesh
= "mcl_signs_sign45.obj"
422 minetest
.register_node("mcl_signs:standing_sign45", ssign45
)
425 local ssign67
= table.copy(ssign
)
426 ssign67
.mesh
= "mcl_signs_sign67.5.obj"
427 minetest
.register_node("mcl_signs:standing_sign67_5", ssign67
)
430 minetest
.register_entity("mcl_signs:text", {
431 collisionbox
= { 0, 0, 0, 0, 0, 0 },
432 visual
= "upright_sprite",
435 collide_with_objects
= false,
437 _signnodename
= nil, -- node name of sign node to which the text belongs
439 on_activate
= function(self
, staticdata
)
440 if staticdata
~= nil and staticdata
~= "" then
441 local des
= minetest
.deserialize(staticdata
)
443 self
._signnodename
= des
._signnodename
446 local meta
= minetest
.get_meta(self
.object
:getpos())
447 local text
= meta
:get_string("text")
448 self
.object
:set_properties({
449 textures
={generate_texture(create_lines(text
), self
._signnodename
)},
451 self
.object
:set_armor_groups({ immortal
= 1 })
453 get_staticdata
= function(self
)
454 local out
= { _signnodename
= self
._signnodename
}
455 return minetest
.serialize(out
)
459 minetest
.register_craft({
461 recipe
= "mcl_signs:wall_sign",
465 if minetest
.get_modpath("mcl_core") then
466 minetest
.register_craft({
467 output
= 'mcl_signs:wall_sign 3',
469 {'group:wood', 'group:wood', 'group:wood'},
470 {'group:wood', 'group:wood', 'group:wood'},
471 {'', 'mcl_core:stick', ''},
476 if minetest
.get_modpath("doc") then
477 doc
.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign")
478 doc
.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign22_5")
479 doc
.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign45")
480 doc
.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign67_5")
483 minetest
.register_alias("signs:sign_wall", "mcl_signs:wall_sign")
484 minetest
.register_alias("signs:sign_yard", "mcl_signs:standing_sign")
487 if minetest
.settings
:get_bool("log_mods") then
488 minetest
.log("action", "[mcl_signs] loaded")