You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

230 lines
6.1 KiB

1 year ago
-- 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),
1 year ago
vector.new(-1, 0, 0),
vector.new(0, 0, 1),
vector.new(1, 0, 0),
1 year ago
}
local down = vector.new(0, -1, 0)
local solid_drawtypes = {
normal = true,
glasslike_framed = true,
}
local S = ("S"):byte(1)
local W = ("W"):byte(1)
local E = ("E"):byte(1)
local C = ("C"):byte(1)
--[[
E: empty
Stype: a go stone of some type
W: full block wall
WE0-WE3: edge concrete
WC0-WC3: corner concrete
--]]
local function _check_position_uncached(pos)
local node = minetest.get_node(pos)
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.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
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:byte(1) == W and under:len() == 3 then
local corner = (under:byte(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 not (check_position(pos + directions[i]):byte(1) == W) then
neighbors[#neighbors+1] = i
end
end
end
return neighbors
end
--[[
Scans for a connected group, until enumerating the whole group, or
finding a liberty. Returns list of captured nodes, or empty list if
no captures.
--]]
local function check_captures(pos)
local group = {pos}
local stones = {[minetest.hash_node_position(pos)] = pos}
local checked = {}
local team = check_position(pos):sub(2,-1)
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)
if newnode:byte(1) == E then
return {capture = false, stones = stones}
elseif newnode:byte(1) == S and
newnode:sub(2, -1) == team then
stones[newhash] = newpos
group[#group+1] = newpos
else
checked[newhash] = true
end
end
end
end
1 year ago
return {capture = true, stones = stones}
1 year ago
end
function lc_liberties.handle_placement(pos)
cache = {}
--minetest.chat_send_all(tostring(pos))
--nodecore.node_sound(pos, "dug")
1 year ago
local our_stone = check_position(pos)
if not (our_stone:byte(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:byte(1) == S) and (not (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
1 year ago
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
nodecore.set_loud(v2, {name = "nc_fire:fire"})
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
1 year ago
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
1 year ago
end
end