local debug_enabled=minetest.settings:get('deck_debugmessages') -- output to minetest.log if debug messages are enabled in settings local function log(txt) if debug_enabled then minetest.log(txt) end end -- describe contents of an unknown something. Used for learning minetest lua api. local function describe(prefix,stuff) local t=type(stuff) if(t == "table") then for key,value in pairs(stuff) do log(prefix.." "..key .. "=".. type(value)) describe(prefix.." ",value) end elseif(t=="nil" or t=="userdata") then log(prefix.." "..t) elseif(t=="number" or t=="string" or t=="boolean") then log(prefix.." "..t..": "..stuff) else log(t) end end -- Groups given to individual cards. Make cards easy to handle. local cardgroups = { crumbly=1, falling_node=1, dig_immediate=3, card=1 } -- Groups given to all types of piles of cards. local pilegroups = { crumbly=1, dig_immediate=3 } -- Names of card suits local suit = { "club","diamond","heart","spade" } -- Used to add colorize to card ranks when creating textures. Diamonds -- and hearts get first colorize from suitcolorize array and clubs -- and spages get the second. local suitcolorizeindex={ 2,1,1,2} -- Colorize rules for red and black card textures. local suitcolorize = { "\\^[colorize\\:#ff0000\\:alpha","\\^[colorize\\:#000000\\:alpha" } -- Dye colors that are not accepted by colorize must be set explicitly local tablecolorize = { dark_grey="#222222",dark_green="#002200" } -- Names of card ranks. These are used in descriptions of cards in inventory. local rank = { "Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten", "Jack","Queen","King"} -- One character symbol for card ranks. These are used in -- card_shortname and png file names in textures. local rank_short = { "a","2","3","4","5","6","7","8","9","t","j","q","k" } -- card_shortname is two character name of card: rank and suit, for example "ah" local card_shortname = {} -- Array of strings passed to register_node as tiles local textures = {} -- Array of strings. Long names of cards, such as "Ace of hearts" local descriptions = {} -- Array of minetest node names, such as "deck:card_ah" local cardnames = {} -- Prefix of all individual card node names. local cardprefix="deck:card_" local cardprefixlen=string.len(cardprefix) -- Prefix of cardtable types local tableprefix="deck:cardtable_" local tableprefixlen=string.len(tableprefix) -- Table of card descriptions. Key is minetest node name, value is -- verbose card name, for example deck:card_ah -> Ace of hearts local carddescriptions = {} -- Array of arrays. Index of outer array is card rank (ace=1, ..., -- king=13). Inner arrays contain strings of format "x,y" where x,y -- are coordinates where to place the suite symbol on the card -- texture. local texturepositions = { -- These are a bit crowded looking positions for 128x128 texture -- {"50,50"}, -- a -- {"50,15","50,65"}, -- 2 -- {"50,2","50,42","50,84"}, -- 3 -- {"30,12","30,62","70,12","70,62"}, -- 4 -- {"30,2","80,2","60,40","35,75","85,75"}, -- 5 -- {"30,2","30,42","30,84","70,2","70,42","70,84"}, -- 6 -- {"40,2","40,42","40,84","80,2","80,42","80,84","5,62"}, -- 7 -- {"40,2","40,42","40,84","80,2","80,42","80,84","2,84","2,42"}, -- 8 -- {"48,2","48,42","48,84","88,2","88,42","88,84","10,84","10,42","10,2"}, -- 9 -- {"48,2","48,42","48,84","88,2","88,42","88,84","10,84","10,42","20,12","60,52"}, -- 10 -- {"33,1"}, -- J -- {"33,1"}, -- Q -- {"33,1"}, -- K {"32,28"}, -- a {"32,28"}, -- 2 {"32,28"}, -- 3 {"32,28"}, -- 4 {"32,28"}, -- 5 {"32,28"}, -- 6 {"32,28"}, -- 7 {"32,28"}, -- 8 {"32,28"}, -- 9 {"32,28"}, -- 10 {"32,28"}, -- J {"32,28"}, -- Q {"32,28"}, -- K } -- Fill the following arrays: -- card_shortname -- descriptions -- textures local function generate_descriptions() local i=1 local s, r for s=1,4,1 do for r=1,13,1 do descriptions[i]=rank[r].." of "..suit[s].."s" card_shortname[i]=rank_short[r]..string.sub(suit[s],1,1) textures[i]="deck_white.png^[resize:96x96^[combine:96x96" for p=1,table.getn(texturepositions[r]),1 do textures[i]=textures[i]..":"..texturepositions[r][p].."=deck_"..suit[s]..".png" end textures[i]=textures[i].. ":3,28=deck_"..rank_short[r]..".png"..suitcolorize[suitcolorizeindex[s]] log(i.." "..textures[i]) i=i+1 end end end -- Add card cardname to main list of pile inventory inv. -- Card is placed on the top of the pile. -- return true if card was added, false if list was full local function pile_add_card(inv,cardname) local s=inv:get_size("main") local place=s local i for i=1,s,1 do local stack=inv:get_stack("main",i) if stack:get_count() > 0 then place=i-1 break end end if place > 0 then inv:set_stack("main",place,cardname) return true end return false end -- Add card from pos to main list of pile inventory inv -- return true if card was added, false if list was full local function pile_add_card_from_pos(inv,pos) local cardname=ItemStack(minetest.get_node(pos).name) return pile_add_card(inv,cardname) end -- Convert the card at pos into stockpile or chestpile local function flip_card(pos,formname,fields,sender) local n=minetest.get_node(pos) local pile if fields.to_stockpile then pile="deck:stockpile" elseif fields.to_chestpile then pile="deck:chestpile" else log("not flipped") return end log(n.name.." to "..pile) minetest.remove_node(pos) minetest.place_node(pos,{name=pile}) local meta=minetest.get_meta(pos) local inv=meta:get_inventory() pile_add_card(inv,n.name) minetest.close_formspec(sender:get_player_name(),"") end -- Add formspec to card at pos to enable flipping it. Used as -- on_construct function for cards. local function on_construct_card(pos) local meta=minetest.get_meta(pos) meta:set_string("formspec", "size[3,3]".. "label[0,0;Flip card to make a]".. "button[0,1;2,1;to_stockpile;Stockpile]".. "button[0,2;2,1;to_chestpile;Chestpile]") end generate_descriptions() for i = 1,table.getn(textures),1 do local name=cardprefix..card_shortname[i] table.insert(cardnames,name) carddescriptions[name]=descriptions[i] minetest.register_node(name, { description = descriptions[i], groups = cardgroups, palette = "deck_palette.png", paramtype2 = "color", drawtype = "normal", param2 = 4, on_construct = on_construct_card, on_receive_fields = flip_card, tiles = { textures[i] } }) end -- Set inventory size of card pile at pos. Used by on_construct -- funtion of card piles. local function pile_inv_setup(pos, placer, itemstack, pointed_thing) local meta=minetest.get_meta(pos) local inv=meta:get_inventory() local invsize=13*4 if inv:set_size("main",invsize) then inv:set_width("main",13) log("deck_setup: size set to "..invsize) else log("deck_setup: failed to set size") end end -- Take and return a card from pile inventory inv. Or nil if pile is -- empty. local function pile_pop(inv) local s=inv:get_size("main") local i for i=1,s,1 do local stack=inv:get_stack("main",i) if stack:get_count() > 0 then local item=stack:take_item(1) inv:set_stack("main",i,stack) return item end end end -- Return, but do not remove topmost card of pile inventory inv. local function pile_peek_first(inv) local s=inv:get_size("main") for i=1,s,1 do local stack=inv:get_stack("main",i) if stack:get_count() > 0 then return stack:get_name() end end end -- Add card to pile inventory inv. -- Return true if if card was added or false if pile was full local function add_to_inv(inv, cardname) local s=inv:get_size("main") for i=1,s,1 do local stack=inv:get_stack("main",i) if stack:get_count() == 0 then log(cardname) inv:set_stack("main",i,ItemStack(cardname)) return true end end return false end -- Make card pile dark so players can easily see that it is empty. -- This should only be used with already empty piles. local function darken_pile(pos) local node=minetest.get_node(pos) node.param2=1 minetest.set_node(pos,node) end -- Change card pile to normal brightness so players can easily see that it is not empty. -- This should be used just before the first card is added to pile. local function lighten_pile(pos) local node=minetest.get_node(pos) node.param2=0 minetest.set_node(pos,node) end -- Move all cards in the pile at pos to end of inventory list. Does -- not change the order of the cards. local function defragment_pile(pos) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() local s=pile_inv:get_size("main") local search_start=s-1 for i=s,1,-1 do log("defrag:"..i) local stack=pile_inv:get_stack("main",i) if stack:is_empty() then for j=search_start,1,-1 do local st=pile_inv:get_stack("main",j) if st:is_empty() then log("defrag: empty stack i="..i.." j="..j) else pile_inv:set_stack("main",i,st) pile_inv:set_stack("main",j,ItemStack()) log("defrag:move"..j.."->"..i) search_start=j-1 if search_start==0 then log("defrag:exit at i="..i) return end break end if j==1 then log("defrag:exit at j=1") return end end elseif search_start > i-1 then search_start=i-1 end end end -- Takes all cards from player main inventory list and places them on the pile at pos local function put_all_to_pile(pos,formname,fields,sender) local player_inv=minetest.get_inventory({type="player", name=sender:get_player_name()}) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() local s=player_inv:get_size("main") local i if pile_peek_first(pile_inv) == nil then lighten_pile(pos) end for i=1,s,1 do local stack=player_inv:get_stack("main",i) if stack:get_count() > 0 then local cardname=stack:get_name() if string.sub(cardname,1,cardprefixlen) == cardprefix then local item=stack:take_item(1) if item ~= nil then add_to_inv(pile_inv, cardname) player_inv:set_stack("main",i,stack) end end end end if pile_peek_first(pile_inv) == nil then darken_pile(pos) else defragment_pile(pos) end end -- Takes one card from pile at pos and adds it to main list of player inventory. local function draw_one_card(pos,formname,fields,sender) local player_inv=minetest.get_inventory({type="player", name=sender:get_player_name()}) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() if pile_inv == nil then log("pile inv=nil") return end local stack=pile_pop(pile_inv) if stack == nil then log("empty pile") darken_pile(pos) return false else player_inv:add_item("main",stack) if pile_peek_first(pile_inv) == nil then darken_pile(pos) else defragment_pile(pos) end return true end end local function suit_to_number(s) local c=string.sub(s,1,1) if c == "c" then return 1 elseif c == "d" then return 2 elseif c == "h" then return 3 elseif c == "s" then return 4 end end local function rank_to_number(r) if r == "a" then return 1 elseif r == "t" then return 10 elseif r == "j" then return 11 elseif r == "q" then return 12 elseif r == "k" then return 13 end return tonumber(r) end local function card_order(a,b) log("comparing "..a.." with "..b) -- deck:card_ZZ local as=string.sub(a,12,12) local bs=string.sub(b,12,12) log("as="..as) log("bs="..bs) if as ~= bs then return suit_to_number(as) < suit_to_number(bs) end local ar=string.sub(a,11,11) local br=string.sub(b,11,11) return rank_to_number(ar) < rank_to_number(br) end -- Sorts card pile at pos local function sort_pile(pos,formname,fields,sender) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() local s=pile_inv:get_size("main") local places={} local stacks={} local names={} for i=1,s,1 do local stack=pile_inv:get_stack("main",i) if stack:is_empty() then log("empty slot "..i) else local n=stack:get_name() table.insert(places, i) stacks[n]=stack table.insert(names, n) end end table.sort(names,card_order) table.sort(places) for i,p in ipairs(places) do pile_inv:set_stack("main",p,stacks[names[i]]) end end -- Shuffles card pile at pos. local function shuffle_pile(pos,formname,fields,sender) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() local s=pile_inv:get_size("main") local newpos={} local i for i=1,s,1 do local try=math.random(s) while newpos[try] ~= nil do try=try+1 if try > s then try=1 end end newpos[try]=pile_inv:get_stack("main",i) end for i=1,s,1 do pile_inv:set_stack("main",i,newpos[i]) end defragment_pile(pos) end -- Convert pile of cards at pos to pile type nodename. local function convert_pile(pos,nodename) local oldmeta=minetest.get_meta(pos) local oldinv=oldmeta:get_inventory() local s=oldinv:get_size("main") local cards={} for i=1,s,1 do local stack=oldinv:get_stack("main",i) cards[i]=stack:get_name() end minetest.remove_node(pos) minetest.place_node(pos,{name=nodename}) local newmeta=minetest.get_meta(pos) local newinv=newmeta:get_inventory() local s=oldinv:get_size("main") for i=1,s,1 do local stack=ItemStack(cards[i]) newinv:set_stack("main",i,stack) end defragment_pile(pos) end -- return position of the first block of cardtable that spans pos local function cardtable_get_main_pos(pos) local meta=minetest.get_meta(pos) local tmx=meta:get_string("cardtable_main_x") local tmz=meta:get_string("cardtable_main_z") return {x=tmx,y=pos.y,z=tmz} end -- return array of positions that list all blocks in a cardtable local function cardtable_get_positions(pos,color) local mainpos=cardtable_get_main_pos(pos) local mainmeta=minetest.get_meta(mainpos) local allpos=minetest.deserialize(mainmeta:get_string("cardtable_positions")) if color==nil then return allpos end local colored={} local name=tableprefix..color for i,p in ipairs(allpos) do local n=minetest.get_node(p) if n.name == name then table.insert(colored,p) end end log(table.getn(colored).." "..color.." cardtable pieces") return colored end -- Take all cards from cardtable at pos and place them in list of sender local function cardtableto(list,pos,sender) local tablepositions=cardtable_get_positions(pos) for i,p in ipairs(tablepositions) do log("checking "..minetest.pos_to_string(p)) local abovepos={x=p.x, y=p.y+1, z=p.z} local abovenode=minetest.get_node(abovepos) local n=abovenode.name if string.sub(n,1,cardprefixlen) == cardprefix then local player_inv=minetest.get_inventory({type="player", name=sender:get_player_name()}) player_inv:add_item(list,ItemStack(n)) minetest.remove_node(abovepos) else log("not card, but "..n) end end end local function cardtable_try_add_card(pos,cardname) local node=minetest.get_node(pos) if node.name == "air" then log("minetest.place_node("..minetest.pos_to_string(pos)..","..cardname..")") minetest.place_node(pos,{name=cardname}) return true end log("not air but "..node.name.." at "..minetest.pos_to_string(pos)..". Didn't place "..cardname) return false end -- Adds card to cardtable. Return true if card was added or false if cardtable was full. local function cardtable_add_card(pos,cardname,color) local above={x=pos.x,y=pos.y+1,z=pos.z} local positions=cardtable_get_positions(pos,color) if debug_enabled then if color == nil then log("adding to cardtable, no color set") else log("adding to color "..color) end end for i,p in ipairs(positions) do above={x=p.x,y=p.y+1,z=p.z} if cardtable_try_add_card(above,cardname) then return true end end return false end -- Takes one card from player and places it on cardtable at pos. Return true if card was moved. local function player_to_cardtable(player_inv,list,i,pos) local stack=player_inv:get_stack(list,i) if stack:get_count() > 0 then local cardname=stack:get_name() if string.sub(cardname,1,cardprefixlen) == cardprefix then local item=stack:take_item(1) if item ~= nil then log("adding "..cardname.." to cardtable at "..minetest.pos_to_string(pos)) if cardtable_add_card(pos,cardname) then player_inv:set_stack(list,i,stack) return true end else log("couldn't tak_item "..cardname.." at position "..i.." list "..list) end else log("not a card but "..cardname.." at position "..i.." list "..list) end else log("empty at position "..i.." of list "..list) end return false end -- Takes card(s) which of list of sender and place them on cardtable at pos. local function inventory_to_cardtable(list,pos,sender,which) local player_inv=minetest.get_inventory({type="player", name=sender:get_player_name()}) local s=player_inv:get_size(list) if which == nil then log("play all cards from "..list) local success=false for i=1,s,1 do if player_to_cardtable(player_inv,list,i,pos) then success=true end end return success elseif which == 1 then log("play first card from "..list) for i=1,s,1 do if player_to_cardtable(player_inv,list,i,pos) then return true end end elseif which == -1 then log("play last card from "..list) for i=s,1,-1 do if player_to_cardtable(player_inv,list,i,pos) then return true end end end return false end local function is_cardtable(pos) local n=minetest.get_node(pos) if string.sub(n.name,1,string.len(tableprefix)) == tableprefix then return true end return false end -- return true if pos is deal position local function is_deal_position(pos) local around={ {x=pos.x-1, y=pos.y, z=pos.z-1},{x=pos.x,y=pos.y,z=pos.z-1},{x=pos.x+1,y=pos.y,z=pos.z-1}, {x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x+1,y=pos.y,z=pos.z}, {x=pos.x-1, y=pos.y, z=pos.z+1},{x=pos.x,y=pos.y,z=pos.z+1},{x=pos.x+1,y=pos.y,z=pos.z+1} } local tablenodes=0 for key,p in ipairs(around) do if is_cardtable(p) then tablenodes=tablenodes+1 end end return tablenodes==1 end -- return list of positions where cards should be dealt local function cardtable_get_deal_positions(pos) local positions=cardtable_get_positions(pos) local dealpositions={} for i,p in ipairs(positions) do if is_deal_position(p) then table.insert(dealpositions,p) else log(minetest.pos_to_string(p).." is not deal position") end end return dealpositions end local function deal_card_to_table(pos,formname,fields,sender) log("deal cards at "..minetest.pos_to_string(pos)) local below={x=pos.x,y=pos.y-1,z=pos.z} local dp if is_cardtable(below) then log("below is cardtable") local from_pile_meta=minetest.get_meta(pos) local from_pile_inv=from_pile_meta:get_inventory() if from_pile_inv == nil then log("pile inv=nil") return false end local stack=pile_pop(from_pile_inv) if stack == nil then log("out of cards") return false end if cardtable_add_card(below,stack:get_name(),fields.deal_to_table) then return true else log("Card did not fit on table.") pile_add_card(from_pile_inv,stack:get_name()) return false end else log("below is not cardtable -> not implemented") return end end -- Deal cards from pile to some other piles on a table local function deal_cards(pos,formname,fields,sender) log("deal cards at "..minetest.pos_to_string(pos)) local below={x=pos.x,y=pos.y-1,z=pos.z} local dp if is_cardtable(below) then log("below is cardtable") dp=cardtable_get_deal_positions(below) else log("below is not cardtable -> not implemented") dp=cardtable_get_deal_positions(pos) end for i,p in ipairs(dp) do log("deal to "..minetest.pos_to_string(p)) local from_pile_meta=minetest.get_meta(pos) local from_pile_inv=from_pile_meta:get_inventory() if from_pile_inv == nil then log("pile inv=nil") return false end local stack=pile_pop(from_pile_inv) if stack == nil then log("out of cards") return false end local target_pos={x=p.x,y=p.y+1,z=p.z} local target_node=minetest.get_node(target_pos) if target_node.name == "air" then minetest.place_node(target_pos,{name="deck:chestpile"}) target_node=minetest.get_node(target_pos) end if target_node.name == "deck:chestpile" or target_node.name == "deck:stockpile" then local target_pile_meta=minetest.get_meta(target_pos) local target_pile_inv=target_pile_meta:get_inventory() if pile_peek_first(target_pile_inv) == nil then lighten_pile(target_pos) target_pile_meta=minetest.get_meta(target_pos) target_pile_inv=target_pile_meta:get_inventory() end pile_add_card(target_pile_inv,stack:get_name()) else log("not pile or air but "..target_node.name.." at "..minetest.pos_to_string(target_pos)) end end end -- Process form button presses of cardtable local function fields_cardtable(pos,formname,fields,sender) if fields.cardtabletomain then log("take all cards") cardtableto("main",pos,sender) elseif fields.cardtabletocraft then cardtableto("craft",pos,sender) elseif fields.main_to_cardtable_all then inventory_to_cardtable("main",pos,sender,nil) elseif fields.craft_to_cardtable_all then inventory_to_cardtable("craft",pos,sender,nil) elseif fields.main_to_cardtable_first then inventory_to_cardtable("main",pos,sender,1) elseif fields.main_to_cardtable_last then inventory_to_cardtable("main",pos,sender,-1) elseif fields.craft_to_cardtable_first then inventory_to_cardtable("craft",pos,sender,1) elseif fields.craft_to_cardtable_last then inventory_to_cardtable("craft",pos,sender,-1) end end -- Process form button presses of card pile local function draw_card(pos,formname,fields,sender) if fields.draw then return draw_one_card(pos,formname,fields,sender) elseif fields.drawall then while draw_one_card(pos,formname,fields,sender) do end elseif fields.shuffle then shuffle_pile(pos,formname,fields,sender) elseif fields.sort then sort_pile(pos,formname,fields,sender) elseif fields.tostock then minetest.close_formspec(sender:get_player_name(),"") convert_pile(pos,"deck:stockpile") elseif fields.alltopile then put_all_to_pile(pos,formname,fields,sender) elseif fields.deal then deal_cards(pos,formname,fields,sender) elseif fields.deal_to_table then deal_card_to_table(pos,formname,fields,sender) end end -- when digging a card pile, place all individual cards to player inventory (not used) local function on_dig_pile_getcards(pos,node,digger) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() local player_inv=minetest.get_inventory({type="player", name=digger:get_player_name()}) local stack=pile_pop(pile_inv) while stack ~= nil do player_inv:add_item("main",stack) stack=pile_pop(pile_inv) end minetest.remove_node(pos) end -- when digging a card pile, place the pile to player inventory local function on_dig_pile_getpile(pos,node,digger) local meta=minetest.get_meta(pos) local pile_inv=meta:get_inventory() local player_inv=minetest.get_inventory({type="player", name=digger:get_player_name()}) local new_chestpile=ItemStack("deck:chestpile") local stackmeta=new_chestpile:get_meta() local i=1 local cardstack=pile_pop(pile_inv) if cardstack == nil then stackmeta:set_string("description","Empty pile") minetest.remove_node(pos) return end local descr="" while cardstack ~= nil do local cardname=cardstack:get_name() log("getpile: got "..cardname) stackmeta:set_string(i,cardname) cardstack=pile_pop(pile_inv) descr=descr..", "..carddescriptions[cardname] i=i+1 end stackmeta:set_string("description",string.sub(descr,3)) player_inv:add_item("main",new_chestpile) minetest.remove_node(pos) end -- Place cards from itemstack inventory to node inventory when player places a pile. local function after_place_pile(pos,placer,itemstack,pointed_thing) local stackmeta=itemstack:get_meta() local i=1 local card=stackmeta:get_string(i) if card == nil then stackmeta:set_string("description","Empty pile") darken_pile(pos) return end local pilemeta=minetest.get_meta(pos) local pile_inv=pilemeta:get_inventory() local descr="" while card ~= "" do log("after_place_pile: i="..i.." card="..card) pile_add_card(pile_inv,card) descr=descr..", "..carddescriptions[card] i=i+1 card=stackmeta:get_string(i) end stackmeta:set_string("description",string.sub(descr,3)) end -- Log cardtable piece coordinates local function describe_cardtable(pos) local meta=minetest.get_meta(pos) local positions=minetest.deserialize(meta:get_string("cardtable_positions")) for key,p in ipairs(positions) do log("cardtable piece in "..minetest.pos_to_string(p)) end end -- Connect cardtable blocks together by marking main block (=first -- block) position to new block and by adding new block position to -- list of positions. local function add_node_to_cardtable(main_pos,new_pos) log("add "..minetest.pos_to_string(new_pos).." to old cardtable with main pos="..minetest.pos_to_string(main_pos)) local new_meta=minetest.get_meta(new_pos) local main_meta=minetest.get_meta(main_pos) local positions=minetest.deserialize(main_meta:get_string("cardtable_positions")) table.insert(positions,new_pos) main_meta:set_string("cardtable_positions",minetest.serialize(positions)) new_meta:set_string("cardtable_main_x",main_pos.x) new_meta:set_string("cardtable_main_y",main_pos.y) new_meta:set_string("cardtable_main_z",main_pos.z) describe_cardtable(main_pos) end -- When placing a cardtable block, set formspec and check whether this -- block starts a new table or is added to old one. local function on_construct_cardtable(pos) log("construct cardtable") local around={ {x=pos.x-1, y=pos.y, z=pos.z-1},{x=pos.x,y=pos.y,z=pos.z-1},{x=pos.x+1,y=pos.y,z=pos.z-1}, {x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x+1,y=pos.y,z=pos.z}, {x=pos.x-1, y=pos.y, z=pos.z+1},{x=pos.x,y=pos.y,z=pos.z+1},{x=pos.x+1,y=pos.y,z=pos.z+1} } local meta=minetest.get_meta(pos) meta:set_string("formspec", "size[13,12]".. "button[0,0;2,1;cardtabletocraft;Take all cards]".. "button[2,0;2,1;craft_to_cardtable_all;Play all cards]".. "button[4,0;2,1;craft_to_cardtable_first;Play first card]".. "button[6,0;2,1;craft_to_cardtable_last;Play last card]".. "list[current_player;craft;0,1;13,4;]".. "button[0,6;2,1;cardtabletomain;Take all cards]".. "button[2,6;2,1;main_to_cardtable_all;Play all cards]".. "button[4,6;2,1;main_to_cardtable_first;Play first card]".. "button[6,6;2,1;main_to_cardtable_last;Play last card]".. "list[current_player;main;0,7;13,5;]" ) for key,p in ipairs(around) do if is_cardtable(p) then add_node_to_cardtable(cardtable_get_main_pos(p),pos) return end end -- this is a new cardtable log("new cardtable") meta:set_string("cardtable_main_x",pos.x) meta:set_string("cardtable_main_y",pos.y) meta:set_string("cardtable_main_z",pos.z) meta:set_string("cardtable_positions",minetest.serialize({pos})) describe_cardtable(pos) end -- Handle removing block from cardtable. Removing main block removes -- the whole table. local function after_dig_cardtable(pos, oldnode, oldmeta, digger) log("after dig") describe("after_dig_meta",oldmeta) local meta=oldmeta["fields"] local tmx=meta["cardtable_main_x"] local tmy=meta["cardtable_main_y"] local tmz=meta["cardtable_main_z"] if(pos.x==tonumber(tmx) and pos.y==tonumber(tmy) and pos.z==tonumber(tmz)) then log("digged main node of cardtable at "..minetest.pos_to_string(pos)) local positions=minetest.deserialize(meta["cardtable_positions"]) for i,pos in ipairs(positions) do minetest.remove_node(pos) log("removing at "..minetest.pos_to_string(pos).." too.") end else log("digged non-main node of cardtable. main="..tmx..","..tmy..","..tmz.." digged="..minetest.pos_to_string(pos)) end end -- return array of strings that contain colors of cardtable at pos. Each color is returned only once. local function cardtable_get_colors(pos) local tablepositions=cardtable_get_positions(pos) local colors={} local colortable={} local offset=tableprefixlen+1 for i,p in ipairs(tablepositions) do local n=minetest.get_node(p) local color=string.sub(n.name,offset) if colortable[color] == nil then table.insert(colors,color) colortable[color]=1 end end return colors end -- Chestpile is a pile of cards that player can use like a chest. minetest.register_node("deck:chestpile", { description = "Card pile that user has full access to. Works also like a minetest chest.", tiles = { "deck_back.png","deck_side.png" }, palette = "deck_palette.png", groups = pilegroups, paramtype2 = "color", drawtype = "color", on_construct = function(pos) pile_inv_setup(pos) local meta=minetest.get_meta(pos) meta:set_string("formspec", "size[13,11]".. "list[context;main;0,0;13,4;]".. "button[0,4;2,1;draw;Draw]".. "button[2,4;2,1;drawall;Draw all]".. "button[4,4;2,1;alltopile;Add all to pile]".. "button[6,4;2,1;shuffle;Shuffle cards]".. "button[8,4;2,1;sort;Sort cards]".. "button[10,4;2,1;tostock;Make stockpile]".. "list[current_player;main;0,6;13,5;]") end, on_dig = on_dig_pile_getpile, stack_max = 1, after_place_node=after_place_pile, on_receive_fields = draw_card }) -- Stockpile is a pile of cards that players can draw cards from, but not -- see which cards are there. minetest.register_node("deck:stockpile", { description = "Stock pile of cards. Can't be peeked.", groups = pilegroups, tiles = { "deck_back.png" }, palette = "deck_palette.png", paramtype2 = "color", drawtype = "color", on_construct = function(pos) pile_inv_setup(pos) local meta=minetest.get_meta(pos) local below={x=pos.x,y=pos.y-1,z=pos.z} local colors=cardtable_get_colors(below) local dealmenu if table.getn(colors) == 1 then dealmenu="button[10,0;2,1;deal_to_table;Deal to table]" else dealmenu="dropdown[10,0;2;deal_to_table;" for key,value in ipairs(colors) do dealmenu=dealmenu..value.."," end dealmenu=dealmenu..";1]" end meta:set_string("formspec","size[13,6]".. "button[0,0;2,1;draw;Draw]".. "button[2,0;2,1;drawall;Draw all]".. "button[4,0;2,1;alltopile;Add all to pile]".. "button[6,0;2,1;shuffle;Shuffle cards]".. "button[8,0;2,1;deal;Deal cards]".. dealmenu.. "list[current_player;main;0,1;13,5;]" ) end, on_dig = on_dig_pile_getpile, stack_max = 1, after_place_node=after_place_pile, on_receive_fields = draw_card }) -- Cardtable is a table building block that should make handling -- multiple cards easier. log(table.getn(dye.dyes).." cardtable colors") for _, row in ipairs(dye.dyes) do local color = row[1] local name = tableprefix..color local colorize = tablecolorize[color] if colorize == nil then colorize = color end local tile = "deck_table_g.png^[colorize:"..colorize..":127" log(name..": "..tile) minetest.register_node(name, { description = "Table where cards can be easily dealt", tiles = { tile }, on_construct = on_construct_cardtable, groups = {cracky=3, cardtable=1}, after_dig_node = after_dig_cardtable, on_receive_fields = fields_cardtable, }) end -- ABM enables adding cards to a pile by placing card on the pile. minetest.register_abm({ label="Drop a card to pile", nodenames={"deck:chestpile","deck:stockpile"}, neighbors=cardnames, interval=1.0, chance = 1, action=function(pos,node,active_object_count,active_object_count_wider) local above = {x=pos.x,y=pos.y+1,z=pos.z} local abovenode = minetest.get_node(above) if string.sub(abovenode.name,1,cardprefixlen) ~= cardprefix then return end local meta=minetest.get_meta(pos) local inv=meta:get_inventory() if pile_peek_first(inv) == nil then lighten_pile(pos) end if pile_add_card_from_pos(inv,above) then minetest.remove_node(above) while true do above={x=above.x,y=above.y+1,z=above.z} abovenode = minetest.get_node(above) if string.sub(abovenode.name,1,cardprefixlen) == cardprefix then minetest.spawn_falling_node(above) else return end end end end }) -- a card can be crafted into a stockpile of one card minetest.register_craft({ output = "deck:stockpile", recipe = {{"group:card"}} }) -- a card can be crafted into a chestpile of one card minetest.register_craft({ output = "deck:chestpile", recipe = {{"group:card"}} }) -- card deck of 52 cards can be crafted from two pieces of paper minetest.register_craft({ output = "deck:chestpile", recipe = {{ "default:paper" },{ "default:paper" }} }) -- card table pieces can be crafted from 2x2 grid of wood minetest.register_craft({ output = tableprefix.."white 99", recipe = {{ "group:wood", "group:wood"},{"group:wood","group:wood"}} }) for _, row in ipairs(dye.dyes) do local color = row[1] local name = tableprefix..color local outputstring = name.." 99" local dyetype = "group:dye,color_white" log("registering craft for '"..outputstring.."' and dyetype "..dyetype) minetest.register_craft({ type = "shapeless", output = outputstring, recipe = { "group:dye,color_" .. color, "group:cardtable" } }) end -- Card turns into chestpile or stockpile depending on -- the place in the craft grid. -- Card deck from paper need to be filled with cards here. local function craft_pile(itemstack,player,old_craft_grid,craft_inv,really) --describe("craft",old_craft_grid) -- local s=craft_inv:get_size("craft") local place, cardname local onlycards=true local onlypaper=true for key,value in pairs(old_craft_grid) do --local stack=craft_inv:get_stack("craft",i) local stack=old_craft_grid[key] if stack:is_empty() then --log("empty "..key) else place=key cardname=stack:get_name() if string.sub(cardname,1,10) ~= cardprefix then onlycards=false end if cardname ~= "default:paper" then onlypaper=false end log("craft:"..key.."="..stack:get_name()) end end if (onlycards==false and onlypaper==false) then return nil end if onlypaper then local resultname=itemstack:get_name() if resultname == "deck:chestpile" then local stackmeta=itemstack:get_meta() local i for i=1,52,1 do stackmeta:set_string(i,cardnames[i]) end return itemstack end return nil end local pilename if place < 5 then pilename="deck:chestpile" else pilename="deck:stockpile" end log("place="..place.." pilename="..pilename) local pilestack=ItemStack(pilename) local stackmeta=pilestack:get_meta() stackmeta:set_string(1,cardname) if really then stackmeta:set_string("description",carddescriptions[cardname]) end return pilestack end minetest.register_on_craft(function(itemstack,player,old_craft_grid,craft_inv) return craft_pile(itemstack,player,old_craft_grid,craft_inv,true) end) minetest.register_craft_predict(function(itemstack,player,old_craft_grid,craft_inv) return craft_pile(itemstack,player,old_craft_grid,craft_inv,false) end) -- Enlarge player inventory for easier handling of cards. minetest.register_on_joinplayer(function(player) --log(player:get_inventory_formspec()) player:get_inventory():set_size("main", 60) player:get_inventory():set_size("craft", 60) player:set_inventory_formspec("size[13,11]list[current_player;main;0,3.5;13,5;]list[current_player;craft;3,0;3,3;]listring[]list[current_player;craftpreview;7,1;1,1;]") --log(player:get_inventory_formspec()) end)