-- 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, } 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 return {capture = true, stones = stones} 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 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 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 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