Support numbers 100 to 999

Add support for groups of blocks up to 999. New nodes are defined for
groups of 100 blocks, similar to 10s but with colours encoded in param2
using 3 separate palettes (4 groups per palette, since each group of 100
is checkered with light and dark).

Also add multipart number nodes support, including improved finding of
number nodes and determination of which set of blocks each belongs to
(using 2 upper bits of param2 to encode the direction the number is
extended from the central block).

Signed-off-by: James Hogan <james@albanarts.com>
master
James Hogan 2021-04-06 09:09:10 +01:00
parent 2d901c9fed
commit f6ba2f83fe
No known key found for this signature in database
GPG Key ID: 35CEE4862B1023F2
7 changed files with 282 additions and 24 deletions

View File

@ -51,7 +51,6 @@ Roadmap
-------
Some features I may add to this mod in future include:
- Extend beyond 99 blocks in a group
- Add tools to make things easier and faster, e.g.:
- Extend the group of blocks in the chosen direction
- Rearrange groups of blocks to explore factors and other properties

View File

@ -143,3 +143,101 @@ for i,info in pairs(ten_blocks) do
})
end
end
local hundred_blocks = {
[100] = {
palette = "numeracy_block_100_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[200] = {
qty = 2,
palette = "numeracy_block_100_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[300] = {
qty = 3,
palette = "numeracy_block_100_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[400] = {
qty = 4,
palette = "numeracy_block_100_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[500] = {
qty = 5,
palette = "numeracy_block_500_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[600] = {
qty = 6,
palette = "numeracy_block_500_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[700] = {
palette = "numeracy_block_500_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[800] = {
qty = 8,
palette = "numeracy_block_500_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[900] = {
qty = 3,
palette = "numeracy_block_900_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[901] = {
qty = 3,
palette = "numeracy_block_900_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
[902] = {
qty = 3,
palette = "numeracy_block_900_palette.png",
tile_side = "numeracy_block_white_side.png",
tile_front = "numeracy_block_white_side.png",
},
}
for i,info in pairs(hundred_blocks) do
local qty = info.qty or 1
for j = 0,qty - 1 do
minetest.register_node("numeracy:block_"..tostring(i).."_"..tostring(j), {
description = "Numeracy block "..tostring(i).." ("..tostring(j)..")",
tiles = {
info.tile_side,
info.tile_side,
info.tile_side,
info.tile_side,
info.tile_front,
info.tile_front,
},
palette = info.palette,
groups = { cracky = 2, not_in_creative_inventory = 1 },
drop = "numeracy:block",
drawtype = "nodebox",
node_box = node_box_ten,
connects_to = { "numeracy:block_"..tostring(i).."_"..tostring(j) },
paramtype = "light",
paramtype2 = "colorfacedir",
on_place = numeracy_block_on_place,
after_dig_node = numeracy_block_after_dig_node,
})
end
end

View File

@ -39,11 +39,36 @@ for i = 1,9 do
walkable = false,
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "facedir", -- the upper 3 bits represent direction numbers extend
drawtype = "nodebox",
node_box = node_box_single_centre,
})
minetest.register_node("numeracy:number_right_"..tostring(i), {
description = "Number "..tostring(i).." (right)",
tiles = {
"numeracy_blank.png",
"numeracy_blank.png",
"numeracy_blank.png",
"numeracy_blank.png",
"numeracy_blank.png^[combine:8x16:8,0=numeracy_number_"..tostring(i)..".png^[transformFX",
"numeracy_blank.png^[combine:8x16:8,0=numeracy_number_"..tostring(i)..".png",
},
use_texture_alpha = 'clip',
groups = { cracky = 2, not_in_creative_inventory = 1 },
drop = "",
pointable = false,
walkable = false,
paramtype = "light",
paramtype2 = "facedir", -- the upper 3 bits represent direction numbers extend
drawtype = "nodebox",
node_box = node_box_single_right,
})
end
for i = 0,9 do
for j = 0,9 do
minetest.register_node("numeracy:number_"..tostring(i)..tostring(j), {
description = "Number "..tostring(i)..tostring(j),
@ -62,7 +87,7 @@ for i = 1,9 do
walkable = false,
paramtype = "light",
paramtype2 = "facedir",
paramtype2 = "facedir", -- the upper 3 bits represent direction numbers extend
drawtype = "nodebox",
node_box = node_box_double,

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

View File

@ -1,16 +1,28 @@
local ADJ_LEFT = 1
local ADJ_RIGHT = 2
local ADJ_FRONT = 3
local ADJ_BACK = 4
local ADJ_UP = 5
local ADJ_DOWN = 6
local ADJ_HORIZ = 4
local adjacent_vectors = {
{ x = -1, y = 0, z = 0 },
{ x = 1, y = 0, z = 0 },
{ x = 0, y = -1, z = 0 },
{ x = 0, y = 1, z = 0 },
{ x = 0, y = 0, z = -1 },
{ x = 0, y = 0, z = 1 }
{ x = 0, y = 0, z = 1 },
{ x = 0, y = 1, z = 0 },
{ x = 0, y = -1, z = 0 },
}
NODE_NONE = 0
NODE_BLOCK = 1
NODE_NUMBER = 2
-- Maximum supported number
local max_number = 999
function nodes_test(nodes, pos)
if not nodes[pos.y] then
return NODE_NONE
@ -47,6 +59,23 @@ function get_node_type(node)
end
end
-- index by param2, get adj
local adj_number_lookup = {
[32 + 0] = ADJ_LEFT, [64 + 0] = ADJ_RIGHT,
[32 + 1] = ADJ_BACK, [64 + 1] = ADJ_FRONT,
[32 + 2] = ADJ_RIGHT, [64 + 2] = ADJ_LEFT,
[32 + 3] = ADJ_FRONT, [64 + 3] = ADJ_BACK,
}
local function adj_node_connected(node_type, adj, adj_node_type, param2)
-- block to block
return (node_type == NODE_BLOCK and adj_node_type == NODE_BLOCK) or
-- upwards, block to number
(adj == ADJ_UP and node_type == NODE_BLOCK and adj_node_type == NODE_NUMBER) or
-- horizontally, to number
(adj <= ADJ_HORIZ and node_type ~= NODE_NONE and adj_node_type == NODE_NUMBER and
adj_number_lookup[param2] and adj_number_lookup[param2] == adj)
end
function find_blocks(pos)
-- Construct a list of block nodes
local nodes = {}
@ -79,16 +108,19 @@ function find_blocks(pos)
if nodes_test(nodes, pos2) == NODE_NONE then
node = minetest.get_node(pos2)
local adj_node_type = get_node_type(node);
if (node_type == NODE_BLOCK and adj_node_type == NODE_BLOCK) or
(i == 3 and node_type == NODE_NUMBER and adj_node_type == NODE_BLOCK) or
(i == 4 and node_type == NODE_BLOCK and adj_node_type == NODE_NUMBER) then
nodes_set(nodes, pos2, adj_node_type)
table.insert(unhandled, { pos2, adj_node_type })
if adj_node_connected(node_type, i, adj_node_type, node.param2) then
if adj_node_type == NODE_BLOCK then
count = count + 1
end
-- don't exceed max_number blocks
if adj_node_type == NODE_NUMBER or count <= max_number then
nodes_set(nodes, pos2, adj_node_type)
end
table.insert(unhandled, { pos2, adj_node_type })
allcount = allcount + 1
if allcount > 100 then
-- allow enough extra numbers to be detected for them to be
-- removed beyond max_number
if allcount >= max_number + 4 then
return nodes, count
end
end
@ -99,22 +131,82 @@ function find_blocks(pos)
return nodes, count
end
local function numeracy_add_number(pos, number, facedir)
if number < 10 then
-- pad = nil: center
-- pad = ' ': right
-- pad = '0': zero pad
local function numeracy_add_number(pos, number, facedir, pad)
if number < 10 and pad == nil then
minetest.set_node(pos, {
name = "numeracy:number_centre_"..tostring(number),
param2 = facedir
})
elseif number < 100 then
elseif number < 10 and pad == ' ' then
minetest.set_node(pos, {
name = "numeracy:number_"..tostring(number),
name = "numeracy:number_right_"..tostring(number),
param2 = facedir
})
elseif number < 100 then
local str = tostring(number)
if string.len(str) < 2 then
str = pad..str
end
minetest.set_node(pos, {
name = "numeracy:number_"..str,
param2 = facedir
})
else
-- TODO
end
end
local numeracy_left_vec_by_facedir = {
[0] = vector.new(-1, 0, 0),
[1] = vector.new( 0, 0, 1),
[2] = vector.new( 1, 0, 0),
[3] = vector.new( 0, 0, -1),
}
local function numeracy_add_numbers(pos, number, facedir)
if number < 100 then
return numeracy_add_number(pos, number, facedir)
end
-- get segments of 2 digits (IN REVERSE ORDER!)
local segs = {}
local i = 1
while number > 0 do
segs[i] = number % 100
number = math.floor(number / 100)
i = i + 1
end
local left_vec = numeracy_left_vec_by_facedir[facedir]
local mid = math.ceil(#segs / 2)
-- from middle to most significant segment
for i = mid,#segs do
local node_pos = vector.add(pos, vector.multiply(left_vec, i-mid))
if minetest.get_node(node_pos).name ~= "air" then
goto skip_left
end
local pad = '0'
if i == #segs then
pad = ' '
end
local param2 = facedir
if i ~= mid then
param2 = param2 + 32
end
numeracy_add_number(node_pos, segs[i], param2, pad)
end
::skip_left::
-- from middle to least significant segment
for i = mid-1,1,-1 do
local node_pos = vector.add(pos, vector.multiply(left_vec, i-mid))
if minetest.get_node(node_pos).name ~= "air" then
goto skip_right
end
numeracy_add_number(node_pos, segs[i], facedir + 64, '0')
end
::skip_right::
end
local function nodes_size(nodes, range_min, range_max)
if range_min == nil then
range_min = { x = nil, y = nil, z = nil }
@ -559,14 +651,55 @@ local function numeracy_restyle_blocks(nodes, count, doer)
local order = info.o
local count_in_100 = count % 100
local order_in_100 = order % 100
local count_in_1000 = count % 1000
local order_in_1000 = order % 1000
local count_in_100 = count_in_1000 % 100
local order_in_100 = order_in_1000 % 100
local count_in_10 = count_in_100 % 10
local order_in_10 = order_in_100 % 10
local count_100s_in_1000 = math.floor(count_in_1000/100)*100
local order_100s_in_1000 = math.floor(order_in_1000/100)*100
local count_10s_in_100 = math.floor(count_in_100/10)*10
local order_10s_in_100 = math.floor(order_in_100/10)*10
if order_10s_in_100 < count_10s_in_100 then
if count > max_number then
-- Unsupported, don't change any blocks, just leave it there
elseif order_100s_in_1000 < count_100s_in_1000 then
-- Blocks of 100
if count_100s_in_1000 <= 900 then
local hundred_in_1000 = order_100s_in_1000/100
-- checkerboard pattern
local colour_index = ((math.abs(x) % 2) ~= (math.abs(y) % 2)) ~=
((math.abs(z) % 2) ~= 0)
if colour_index then
colour_index = 1
else
colour_index = 0
end
local block_name = "numeracy:block_"..tostring(count_100s_in_1000).."_"..tostring(hundred_in_1000)
if count_100s_in_1000 == 700 then
block_name = "numeracy:block_"..tostring(100 + order_100s_in_1000).."_0"
if order_100s_in_1000 <= 400 then
colour_index = colour_index + 2*(order_100s_in_1000/100)
else
colour_index = colour_index + 2*(order_100s_in_1000/100 - 4)
end
elseif count_100s_in_1000 == 900 then
local three_hundred = math.floor(order_100s_in_1000/300)
local hundred_in_300 = hundred_in_1000 % 3
block_name = "numeracy:block_"..tostring(900 + three_hundred).."_"..tostring(hundred_in_300)
colour_index = colour_index + 2*three_hundred
elseif count_100s_in_1000 <= 400 then
colour_index = colour_index + 2*(count_100s_in_1000/100 - 1)
elseif count_100s_in_1000 <= 800 then
colour_index = colour_index + 2*(count_100s_in_1000/100 - 5)
end
minetest.set_node(pos, {
name = block_name,
param2 = colour_index*32 + facedir
})
end
elseif order_10s_in_100 < count_10s_in_100 then
-- Blocks of 10
if count_10s_in_100 == 70 then
local tens_for_70 = 10 + order_10s_in_100
@ -632,9 +765,9 @@ local function numeracy_restyle_blocks(nodes, count, doer)
end
end
end
if found_best then
if found_best and count <= max_number then
best_pos.y = best_pos.y + 1
numeracy_add_number(best_pos, count, facedir)
numeracy_add_numbers(best_pos, count, facedir)
end
end
end
@ -667,8 +800,11 @@ function numeracy_block_after_dig_node(pos, oldnode, oldmetadata, digger)
local skips = {}
local positions = {}
for i = 1, 6 do
skips[i] = false;
positions[i] = vector.add(pos, adjacent_vectors[i])
local node = minetest.get_node(positions[i])
local adj_node_type = get_node_type(node);
skips[i] = not (node and adj_node_connected(NODE_BLOCK, i, adj_node_type, node.param2))
end
for i = 1, 6 do
if skips[i] == false then