new game: minesweeper

sokoban: player is teleported to start position when game starts, level clear added
master
rnd1 2016-04-20 13:39:39 +02:00
parent 98c2697736
commit 9a0eebbf63
3 changed files with 273 additions and 10 deletions

View File

@ -3,5 +3,6 @@ dofile(minetest.get_modpath("games").."/maze.lua")
dofile(minetest.get_modpath("games").."/sokoban.lua")
dofile(minetest.get_modpath("games").."/spleef.lua")
dofile(minetest.get_modpath("games").."/checkers.lua")
dofile(minetest.get_modpath("games").."/minesweeper.lua")
dofile(minetest.get_modpath("games").."/life.lua")
print("[games] loaded")

240
minesweeper.lua Normal file
View File

@ -0,0 +1,240 @@
--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("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;
end
end
end
if not finished then
minetest.chat_send_all("games: BOOM! ".. name .. " has failed to detect all the mines! ")
if mpos then minetest.swap_node(mpos,{name = "default:cobble"}) end
minesweeper.games[pname]=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;
effector.action_on(pos,node,16)
end;
meta:set_string("infotext", name .. " just solved minesweeper level");
minesweeper.games[name]=nil;
return
end
end
if not minesweeper.games[name] then -- no game yet, place mines
meta:set_string("player",name);
minesweeper.create_game(pos);
return
end
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, 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 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
return
end
meta:set_int("t",t1);
local ppos = puncher:getpos(); -- player position
dist = math.sqrt((ppos.x-pos.x)^2+(ppos.y-pos.y)^2+(ppos.z-pos.z)^2);
if dist> 1 then -- just probe, count mines
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")
else -- marking blocks 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"})
local gmeta = minetest.get_meta(gpos);
local pname = gmeta:get_string("player");
minesweeper.games[pname]=nil; -- end game
return
end
minetest.set_node(pos,{name = "default:dirt_with_grass"})
end
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;
})
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;
});

View File

@ -1,5 +1,4 @@
-- SOKOBAN GAME
-- basic board game mechanics ( checkers )
-- by rnd
@ -72,14 +71,28 @@ minetest.register_node("games:crate", { -- block that player pushes around, basi
if sokoban.blocks~=0 then
minetest.chat_send_player(name,"move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left ");
else
minetest.chat_send_all("#SOKOBAN : ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
minetest.chat_send_all("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
local meta = minetest.get_meta(sokoban.pos);
meta:set_string("infotext", name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
-- if playerdata~=nil then -- award xp if playerdata exist
-- playerdata[name].xp = playerdata[name].xp + (sokoban.level-0.5)*100
-- end
sokoban.playername = ""; sokoban.level = 1
local imax = meta:get_int("imax"); local jmax = meta:get_int("jmax");
local i,j;
for i = 1,imax do
for j=1,jmax do
minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level
end
end
-- activates block below sokoban game block upon game completion
local pos = {x=sokoban.pos.x,y=sokoban.pos.y-1,z=sokoban.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;
effector.action_on(pos,node,16)
end;
sokoban.playername = ""; sokoban.level = 1
end
end,
})
@ -99,7 +112,7 @@ description = "sokoban crate",
local form =
"size[3,1]" .. -- width, height
"field[0,0.5;3,1;level;enter level 1 to 90;1]"..
"button[2.5,0.25;1,1;OK;OK]"
"button_exit[2.5,0.25;1,1;OK;OK]"
meta:set_string("formspec", form)
meta:set_string("infotext","sokoban level loader, right click to select level")
meta:set_int("time", minetest.get_gametime()-300);
@ -150,13 +163,17 @@ description = "sokoban crate",
if not lvl_found then file:close();return end
sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0;
local imax=0; local jmax = 0;
while str~= nil do
str = file:read("*line");
if str~=nil then
if string.sub(str,1,1)==";" then
file:close(); minetest.chat_send_all("Sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); return
imax=i;
meta:set_int("imax",imax); meta:set_int("jmax", jmax); -- remember game dimensions
file:close(); minetest.chat_send_all("games: sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); return
end
i=i+1;
if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions
for j = 1,string.len(str) do
p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j);
p.y=p.y-1;
@ -167,7 +184,12 @@ description = "sokoban crate",
if s=="$" then minetest.set_node(p,{name="games:crate"});sokoban.blocks=sokoban.blocks+1 end
if s=="." then p.y=p.y-1;minetest.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;minetest.set_node(p,{name="air"}) end
--starting position
if s=="@" then p.y=p.y-1;minetest.set_node(p,{name="default:glass"}); p.y=p.y+1;minetest.set_node(p,{name="air"}) end
if s=="@" then
sender:setpos({x=p.x,y=p.y+3,z=p.z}); -- move player to start position
p.y=p.y-1;minetest.set_node(p,{name="default:glass"});
p.y=p.y+1;minetest.set_node(p,{name="air"})
p.y=p.y+2;minetest.set_node(p,{name="default:ladder"})
end
if s~="@" then p.y = pos.y+2;minetest.set_node(p,{name="games:glass_maze"});
else p.y=pos.y+2;minetest.set_node(p,{name="default:ladder"})
end -- roof above to block jumps