games/minesweeper.lua

274 lines
8.6 KiB
Lua

--Minesweeper
local minesweeper = {};
minesweeper.games = {};-- games in progress
minetest.register_node("games:minesweeper", {
description = "minesweeper",
tiles = {"default_copper_block.png"},
groups = {oddly_breakable_by_hand=2},
is_ground_content = false,
paramtype = "light",
light_source = 10,
sounds = default.node_sound_wood_defaults(),
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos);
local width = 10;meta:set_int("width",width);
local height = 10;meta:set_int("height",height);
local count = 20;meta:set_int("count",count);
meta:set_string("player",""); -- whos playing
meta:set_int("t",0); -- start of game
local form = "size[3,3] field[0.25,0.5;1,1;width;width ;"..width.."]"..
"field[2.25,0.5;1,1;height;height ;"..height.."]" ..
"field[0.25,1.5;1,1;count;count ;"..count.."]" ..
"button_exit[0.,2.5;1,1;OK;OK]";
meta:set_string("formspec", form);
meta:set_string("owner", placer:get_player_name());
meta:set_string("infotext","Minesweeper, punch or activate to start the game");
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then return end
if fields.OK then
local meta = minetest.get_meta(pos);
local width = tonumber(fields.width) or 5;
local height = tonumber(fields.height) or 5;
local count = tonumber(fields.count) or 5;
local form = "size[3,3] field[0.25,0.5;1,1;width;width ;"..width.."]"..
"field[2.25,0.5;1,1;height;height ;"..height.."]" ..
"field[0.25,1.5;1,1;count;count ;"..count.."]" ..
"button_exit[0.,2.5;1,1;OK;OK]";
meta:set_string("formspec", form);
-- clear gamearea
local i,j;
for i=1,width do
for j=1,height do
minetest.set_node({x=pos.x+i+1,y=pos.y,z=pos.z+j+1},{name = "air"});
end
end
meta:set_int("mines",0);
meta:set_int("width",width);
meta:set_int("height",height);
meta:set_int("count", count);
meta:set_string("player","");
meta:set_int("t",0); -- start of game
meta:set_string("infotext", "minesweeper game set. punch block to start game.")
end
end,
on_punch = function(pos, node, puncher, pointed_thing)
if puncher:get_player_control().sneak then return end
local meta = minetest.get_meta(pos);
-- can we start new game? not too soon and player not still playing
local t0 = meta:get_int("t");
local t1 = minetest.get_gametime();
if t1-t0<5 then return end
meta:set_int("t",t1);
local pname = meta:get_string("player");
local name = puncher:get_player_name();
local mines = meta:get_int("mines");
if pname~=name then
local player = minetest.get_player_by_name(pname);
if player then
local p = player:getpos();
local dist = math.sqrt((p.x-pos.x)^2+(p.y-pos.y)^2+(p.z-pos.z)^2);
if dist< 30 then minetest.chat_send_player(name, "games: minesweeper game still in progress") return end
end
elseif minesweeper.games[name] then -- if game in progress end it
local finished = true;
-- check if mines are marked correctly!
local width = meta:get_int("width");
local height = meta:get_int("height");
local count = meta:get_int("count");
local i,j,mpos;
for i=1,width do
for j=1,height do
mpos = {x=pos.x+i+1,y=pos.y,z=pos.z+j+1};
if minetest.get_node(mpos).name ~= "games:markmine" and minetest.get_meta(mpos):get_int("mine")==1 then -- FAIL
finished = false;
elseif minetest.get_node(mpos).name == "games:markmine" and minetest.get_meta(mpos):get_int("mine")==0 then
finished = false;
end
end
end
if not finished then
minetest.chat_send_all("games: BOOM! ".. name .. " has failed to detect the mines properly ! ")
if mpos then minetest.swap_node(mpos,{name = "default:cobble"}) end
minesweeper.games[name]=nil; -- end game
return;
else -- done
-- activates block below minesweeper game block upon game completion
local pos = {x=pos.x,y=pos.y-1,z=pos.z};
local node = minetest.get_node(pos); if not node.name then return end -- error
local table = minetest.registered_nodes[node.name];
if table and table.mesecons and table.mesecons.effector then
local effector=table.mesecons.effector;
if effector.action_on then
effector.action_on(pos,node,16)
end
end;
meta:set_string("infotext", name .. " just solved minesweeper level");
meta:set_string("player","");
minesweeper.games[name]=nil;
return
end
end
-- no game yet, place mines
meta:set_string("player",name);
minesweeper.create_game(pos);
return
end
}
)
minesweeper.create_game = function(pos)
local meta = minetest.get_meta(pos);
local width = meta:get_int("width");
local height = meta:get_int("height");
local count = meta:get_int("count");
local i,j;
for i=1,width do
for j=1,height do
minetest.set_node({x=pos.x+i+1,y=pos.y,z=pos.z+j+1},{name = "games:dirt"}); --??
end
end
local mines = 0;
local mtable = {};
math.randomseed(minetest.get_gametime());
for i=1,count do -- place max up to count mines
local x = math.random(width);
local z = math.random(height);
if not mtable[{x,z}] then
mtable[{x,z}]=true
local mmeta = minetest.get_meta({x=pos.x+x+1,y=pos.y,z=pos.z+z+1});
mmeta:set_int("mine",1);
mines=mines+1;
end
end
local pname = meta:get_string("player");
minesweeper.games[pname] = {pos=pos}; -- position of gameblock
meta:set_int("mines", mines); -- remaining mines
meta:set_string("infotext", "Minesweeper game started by "..pname..", there are ".. mines .. " mines. Mark all mines by double punch from distance. Mark area as safe by standing over it and punching. When you are done punch gameblock.");
minetest.chat_send_player(pname, "game: Minesweeper game started");
end
local punch_minesweep = function(pos, node, puncher, pointed_thing)
-- find closeby game and play
local gpos, dist;
for _,v in pairs(minesweeper.games) do
dist = math.sqrt((v.pos.x-pos.x)^2+(v.pos.y-pos.y)^2+(v.pos.z-pos.z)^2);
if dist<32 then gpos = v.pos end
end
local name = puncher:get_player_name();
if not gpos then
minetest.chat_send_player(name,"games: punch nearby game block for minesweeper to start playing.")
return
end
local meta = minetest.get_meta(pos);
local ppos = puncher:getpos(); -- player position
--dist = math.sqrt((ppos.x-pos.x)^2+(ppos.y-pos.y)^2+(ppos.z-pos.z)^2);
local mines = 0;
for i = -1,1 do
for j = -1,1 do
local mmeta = minetest.get_meta({x=pos.x+i,y=pos.y,z=pos.z+j});
if mmeta:get_int("mine") == 1 then mines = mines +1 end
end
end
--minetest.chat_send_player(name, "Found " .. mines .. " nearby mines. Punch 2x to mark mine")
meta:set_string("infotext", "found ".. mines .. " nearby mines")
-- try mark block as safe?
if meta:get_int("mine")==1 then
minetest.chat_send_all("games: BOOM! ".. name .. " has stepped on mine ")
minetest.swap_node(pos,{name = "games:markmine"})
puncher:setpos({x=gpos.x,y=gpos.y+1,z=gpos.z});
local gmeta = minetest.get_meta(gpos);
local pname = gmeta:get_string("player");
minesweeper.games[name]=nil; -- end game
gmeta:set_string("player","");
-- activates block below minesweeper game with OFF upon fail
gpos.y=gpos.y-1;
local node = minetest.get_node(gpos); if not node.name then return end -- error
local table = minetest.registered_nodes[node.name];
if table and table.mesecons and table.mesecons.effector then
local effector=table.mesecons.effector;
if effector.action_off then
effector.action_off(gpos,node,16)
end
end;
return
else
minetest.swap_node(pos,{name = "default:dirt_with_grass"})
end
end
local reveal_minesweep = function(pos, node, player, itemstack, pointed_thing)
-- local t0 = meta:get_int("t");
-- local t1 = minetest.get_gametime();
-- if t1-t0<1 then -- un/mark mine
if node.name == "games:dirt" then
minetest.swap_node(pos,{name = "games:markmine"})
else
minetest.swap_node(pos,{name = "games:dirt"})
end
--meta:set_int("t",t1);
end
minetest.register_node("games:dirt", {
description = "Dirt with mines, punch from far to check for mines, stand near and punch to clear mine",
tiles = {"default_dirt.png"},
groups = {immortal = 1},
sounds = default.node_sound_dirt_defaults(),
on_punch = punch_minesweep,
on_rightclick = reveal_minesweep
})
minetest.register_node("games:markmine", {
description = "Dirt with mines, punch from far to check for mines, stand near and punch to clear mine",
tiles = {"default_lava.png"},
groups = {immortal = 1},
sounds = default.node_sound_dirt_defaults(),
on_punch = punch_minesweep,
on_rightclick = reveal_minesweep
});