|
|
|
@ -25,23 +25,21 @@ local solid_drawtypes = { |
|
|
|
|
glasslike_framed = true, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
local S = ("S"):byte(1) |
|
|
|
|
local W = ("W"):byte(1) |
|
|
|
|
local E = ("E"):byte(1) |
|
|
|
|
local C = ("C"):byte(1) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--[[ |
|
|
|
|
E: air |
|
|
|
|
E_: other empty |
|
|
|
|
Etype: territory marker of some type |
|
|
|
|
E: empty |
|
|
|
|
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 |
|
|
|
@ -52,26 +50,18 @@ local function _check_position_uncached(pos) |
|
|
|
|
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_" |
|
|
|
|
return "E" |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
@ -90,8 +80,8 @@ end |
|
|
|
|
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") |
|
|
|
|
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 |
|
|
|
@ -116,7 +106,7 @@ 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 |
|
|
|
|
if not (check_position(pos + directions[i]):byte(1) == W) then |
|
|
|
|
neighbors[#neighbors+1] = i |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
@ -125,11 +115,16 @@ local function neighbor_dirs(pos) |
|
|
|
|
return neighbors |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
local function connected_search(pos, final_result, early_termination_filter) |
|
|
|
|
--[[ |
|
|
|
|
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 piece = check_position(pos) |
|
|
|
|
local team = check_position(pos):sub(2,-1) |
|
|
|
|
|
|
|
|
|
local probe = 0 |
|
|
|
|
while probe < #group do |
|
|
|
@ -143,14 +138,10 @@ local function connected_search(pos, final_result, early_termination_filter) |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
@ -160,70 +151,16 @@ local function connected_search(pos, final_result, early_termination_filter) |
|
|
|
|
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 |
|
|
|
|
) |
|
|
|
|
return {capture = true, stones = stones} |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
function lc_liberties.handle_placement(pos) |
|
|
|
|
cache = {} |
|
|
|
|
--minetest.chat_send_all(tostring(pos)) |
|
|
|
|
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 |
|
|
|
|
if not (our_stone:byte(1) == S) then |
|
|
|
|
return |
|
|
|
|
end |
|
|
|
|
our_stone = our_stone:sub(2, -1) |
|
|
|
@ -244,7 +181,7 @@ function lc_liberties.handle_placement(pos) |
|
|
|
|
|
|
|
|
|
if not checked then |
|
|
|
|
local node = check_position(new_pos) |
|
|
|
|
if (node:sub(1, 1) == "S") and (node:sub(2, -1) ~= our_stone) then |
|
|
|
|
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 |
|
|
|
@ -255,78 +192,22 @@ function lc_liberties.handle_placement(pos) |
|
|
|
|
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 |
|
|
|
|
local proximal = {} |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
nodecore.set_loud(v2, {name = "nc_fire:fire"}) |
|
|
|
|
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 |
|
|
|
|
-- todo: spawn itemstack of captured stones somehow |
|
|
|
|
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) |
|
|
|
|
cache = {} |
|
|
|
|
|
|
|
|
|
if placer.get_player_control and placer:get_player_control().aux1 then |
|
|
|
|
return minetest.item_place(itemstack, placer, pointed_thing) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if pointed_thing and pointed_thing.above then |
|
|
|
|
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) |
|
|
|
|
minetest.chat_send_all("You gotcher self a suicide move right there! size: "..#(captures.stones)) |
|
|
|
|
-- todo: replace with itemstack of one stone |
|
|
|
|
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 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 |
|
|
|
|