|
|
|
-- LUALOCALS < ---------------------------------------------------------
|
|
|
|
local include, nodecore, pairs, string, table
|
|
|
|
= include, nodecore, pairs, string, table
|
|
|
|
local string_lower
|
|
|
|
= string.lower
|
|
|
|
|
|
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local modname = minetest.get_current_modname()
|
|
|
|
|
|
|
|
--nodecore.gametime
|
|
|
|
|
|
|
|
local cache = {}
|
|
|
|
|
|
|
|
local directions = { [0] =
|
|
|
|
vector.new(0, 0, -1),
|
|
|
|
vector.new(-1, 0, 0),
|
|
|
|
vector.new(0, 0, 1),
|
|
|
|
vector.new(1, 0, 0),
|
|
|
|
}
|
|
|
|
|
|
|
|
local down = vector.new(0, -1, 0)
|
|
|
|
|
|
|
|
local solid_drawtypes = {
|
|
|
|
normal = true,
|
|
|
|
glasslike_framed = true,
|
|
|
|
}
|
|
|
|
|
|
|
|
--[[
|
|
|
|
E: air
|
|
|
|
E_: other empty
|
|
|
|
Etype: territory marker of some type
|
|
|
|
Stype: a go stone of some type
|
|
|
|
W: full block wall
|
|
|
|
WE0-WE3: edge concrete
|
|
|
|
WC0-WC3: corner concrete
|
|
|
|
WB: other goban concrete
|
|
|
|
--]]
|
|
|
|
local function _check_position_uncached(pos)
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
|
|
|
|
if node.name == "air" then
|
|
|
|
return "E"
|
|
|
|
end
|
|
|
|
|
|
|
|
local reg_item = minetest.registered_items[node.name]
|
|
|
|
|
|
|
|
if not reg_item then
|
|
|
|
return "W"
|
|
|
|
end
|
|
|
|
|
|
|
|
if reg_item.groups and reg_item.groups.go_stone then
|
|
|
|
return "S" .. reg_item.go_team
|
|
|
|
end
|
|
|
|
|
|
|
|
if reg_item.groups and reg_item.groups.go_territory_marker then
|
|
|
|
return "E" .. reg_item.go_team
|
|
|
|
end
|
|
|
|
|
|
|
|
if reg_item.pattern_def then
|
|
|
|
if reg_item.pattern_def.name == "edgy" then
|
|
|
|
return "WE" .. node.param2
|
|
|
|
elseif reg_item.pattern_def.name == "corny" then
|
|
|
|
return "WC" .. node.param2
|
|
|
|
elseif reg_item.pattern_def.name == "crossy" then
|
|
|
|
return "WB"
|
|
|
|
elseif reg_item.pattern_def.name == "starcrossy" then
|
|
|
|
return "WB"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if solid_drawtypes[reg_item.drawtype] and not reg_item.groups.falling_node then
|
|
|
|
return "W"
|
|
|
|
else
|
|
|
|
return "E_"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function check_position(pos)
|
|
|
|
local hash = minetest.hash_node_position(pos)
|
|
|
|
if cache[hash] then
|
|
|
|
return cache[hash]
|
|
|
|
end
|
|
|
|
|
|
|
|
local ret = _check_position_uncached(pos)
|
|
|
|
cache[hash] = ret
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
|
|
|
-- check for walls, physical or implied
|
|
|
|
local function edge_check(pos, dir, terminate)
|
|
|
|
local under = check_position(pos + down)
|
|
|
|
|
|
|
|
if under:sub(1, 1) == "W" and under:len() == 3 then
|
|
|
|
local corner = (under:sub(2, 2) == "C")
|
|
|
|
local edge_dir = tonumber(under:sub(3, 3))
|
|
|
|
|
|
|
|
if (dir == edge_dir) or (corner and (dir == (edge_dir+1)%4)) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not terminate then
|
|
|
|
if edge_check(pos+directions[dir], (dir+2)%4, true) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
give valid neighbor directions from a position, excluding solid blocks
|
|
|
|
and also respecting borders indicated by goban concrete
|
|
|
|
--]]
|
|
|
|
local function neighbor_dirs(pos)
|
|
|
|
local neighbors = {}
|
|
|
|
for i,v in pairs(directions) do
|
|
|
|
if not edge_check(pos, i) then
|
|
|
|
if check_position(pos + directions[i]):sub(1, 1) ~= "W" then
|
|
|
|
neighbors[#neighbors+1] = i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return neighbors
|
|
|
|
end
|
|
|
|
|
|
|
|
local function connected_search(pos, final_result, early_termination_filter)
|
|
|
|
local group = {pos}
|
|
|
|
local stones = {[minetest.hash_node_position(pos)] = pos}
|
|
|
|
local checked = {}
|
|
|
|
local piece = check_position(pos)
|
|
|
|
|
|
|
|
local probe = 0
|
|
|
|
while probe < #group do
|
|
|
|
probe = probe + 1
|
|
|
|
pos = group[probe]
|
|
|
|
|
|
|
|
for i,v in pairs(neighbor_dirs(pos)) do
|
|
|
|
local newpos = pos + directions[v]
|
|
|
|
local newhash = minetest.hash_node_position(newpos)
|
|
|
|
|
|
|
|
if (not stones[newhash]) and (not checked[newhash]) then
|
|
|
|
local newnode = check_position(newpos)
|
|
|
|
|
|
|
|
local filter_result
|
|
|
|
if early_termination_filter then
|
|
|
|
filter_result = early_termination_filter(newnode)
|
|
|
|
end
|
|
|
|
if filter_result then
|
|
|
|
filter_result.stones = stones
|
|
|
|
return filter_result
|
|
|
|
elseif newnode == piece then
|
|
|
|
stones[newhash] = newpos
|
|
|
|
group[#group+1] = newpos
|
|
|
|
else
|
|
|
|
checked[newhash] = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
final_result = final_result()
|
|
|
|
final_result.stones = stones
|
|
|
|
return final_result
|
|
|
|
end
|
|
|
|
|
|
|
|
local function _check_captures_filter(node)
|
|
|
|
if node:sub(1, 1) == "E" then
|
|
|
|
return {capture = false}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function _check_captures_final()
|
|
|
|
return {capture = true}
|
|
|
|
end
|
|
|
|
|
|
|
|
local function check_captures(pos)
|
|
|
|
return connected_search(pos, _check_captures_final, _check_captures_filter)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function _connected_group_final()
|
|
|
|
return {}
|
|
|
|
end
|
|
|
|
|
|
|
|
local function connected_group(pos)
|
|
|
|
return connected_search(pos, _connected_group_final)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function territory_search(pos, max)
|
|
|
|
if check_position(pos) ~= "E" then
|
|
|
|
return {}
|
|
|
|
end
|
|
|
|
|
|
|
|
local team
|
|
|
|
return connected_search(pos,
|
|
|
|
function()
|
|
|
|
return {team = team}
|
|
|
|
end,
|
|
|
|
function(node)
|
|
|
|
if node == "E" then
|
|
|
|
max = max - 1
|
|
|
|
if max <= 0 then
|
|
|
|
return {}
|
|
|
|
end
|
|
|
|
elseif node ~= "E_" and ((node:sub(1, 1) == "E") or (node:sub(1, 1) == "S")) and node:len() > 1 then
|
|
|
|
node = node:sub(2, -1)
|
|
|
|
if team then
|
|
|
|
if team ~= node then
|
|
|
|
return {}
|
|
|
|
end
|
|
|
|
else
|
|
|
|
team = node
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
function lc_liberties.handle_placement(pos)
|
|
|
|
cache = {}
|
|
|
|
--minetest.chat_send_all(tostring(pos))
|
|
|
|
--nodecore.node_sound(pos, "dug")
|
|
|
|
|
|
|
|
local our_stone = check_position(pos)
|
|
|
|
if our_stone:sub(1, 1) ~= "S" then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
our_stone = our_stone:sub(2, -1)
|
|
|
|
|
|
|
|
local captureses = {}
|
|
|
|
local captured = false
|
|
|
|
for i,v in pairs(neighbor_dirs(pos)) do
|
|
|
|
local new_pos = pos + directions[v]
|
|
|
|
|
|
|
|
local hash = minetest.hash_node_position(new_pos)
|
|
|
|
|
|
|
|
local checked = false
|
|
|
|
for i2,v2 in pairs(captureses) do
|
|
|
|
if v2.stones[hash] then
|
|
|
|
checked = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not checked then
|
|
|
|
local node = check_position(new_pos)
|
|
|
|
if (node:sub(1, 1) == "S") and (node:sub(2, -1) ~= our_stone) then
|
|
|
|
local captures = check_captures(new_pos)
|
|
|
|
captured = captured or captures.capture
|
|
|
|
captureses[#captureses+1] = captures
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if captured then
|
|
|
|
for i,v in pairs(captureses) do
|
|
|
|
if v.capture then
|
|
|
|
local stone = minetest.get_node(v.stones[next(v.stones)]).name
|
|
|
|
local count = 0
|
|
|
|
|
|
|
|
local proximal
|
|
|
|
local proximal_c = 0
|
|
|
|
|
|
|
|
for i2, v2 in pairs(v.stones) do
|
|
|
|
local under = check_position(v2 + down)
|
|
|
|
if (under:sub(1, 1) == "W") and (under:len() > 1) then
|
|
|
|
nodecore.set_loud(v2, {name = "nc_fire:fire"})
|
|
|
|
else
|
|
|
|
nodecore.set_loud(v2, {name = "air"})
|
|
|
|
end
|
|
|
|
count = count + 1
|
|
|
|
if (v2:distance(pos) < 2.3) then
|
|
|
|
proximal_c = proximal_c + 1
|
|
|
|
if math.random(1, proximal_c) == 1 then
|
|
|
|
proximal = v2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local stack_max = minetest.registered_items[stone].stack_max
|
|
|
|
while count > 0 do
|
|
|
|
nodecore.item_eject(proximal, stone .. " " .. math.min(count, stack_max), 3)
|
|
|
|
count = count - stack_max
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
local captures = check_captures(pos)
|
|
|
|
if captures.capture then
|
|
|
|
local stone = minetest.get_node(pos).name
|
|
|
|
nodecore.set_loud(pos, {name = "air"})
|
|
|
|
nodecore.item_eject(pos, stone, 5)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function lc_liberties.handle_territory_fill(itemstack, placer, pointed_thing)
|
|
|
|
if (placer.get_player_control and placer:get_player_control().aux1)
|
|
|
|
or
|
|
|
|
(pointed_thing and pointed_thing.under and minetest.get_node(pointed_thing.under).name == "nc_items:stack")
|
|
|
|
then
|
|
|
|
return minetest.item_place(itemstack, placer, pointed_thing)
|
|
|
|
end
|
|
|
|
|
|
|
|
if pointed_thing and pointed_thing.above then
|
|
|
|
cache = {}
|
|
|
|
|
|
|
|
local territories = territory_search(pointed_thing.above, itemstack:get_count())
|
|
|
|
if territories.team then
|
|
|
|
local piece = modname .. ":territory_" .. territories.team
|
|
|
|
local count = 0
|
|
|
|
for i, v in pairs(territories.stones) do
|
|
|
|
nodecore.set_loud(v, {name = piece})
|
|
|
|
count = count + 1
|
|
|
|
end
|
|
|
|
itemstack:set_count(itemstack:get_count() - count)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return itemstack
|
|
|
|
end
|
|
|
|
|
|
|
|
function lc_liberties.handle_dig(pos, node, digger)
|
|
|
|
cache = {}
|
|
|
|
|
|
|
|
if (digger.get_player_control and digger:get_player_control().aux1)
|
|
|
|
~=
|
|
|
|
(minetest.registered_items[node.name].groups.go_territory_marker ~= nil)
|
|
|
|
then
|
|
|
|
for i, v in pairs(connected_group(pos).stones) do
|
|
|
|
minetest.node_dig(v, node, digger)
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
return minetest.node_dig(pos, node, digger)
|
|
|
|
end
|
|
|
|
end
|