]> git.lizzy.rs Git - xdecor.git/blob - src/chess.lua
New chessboard interface
[xdecor.git] / src / chess.lua
1 local realchess = {}
2 screwdriver = screwdriver or {}
3
4 local function index_to_xy(idx)
5         idx = idx - 1
6         local x = idx % 8
7         local y = (idx - x) / 8
8         return x, y
9 end
10
11 local function xy_to_index(x, y)
12         return x + y * 8 + 1
13 end
14
15 local chat_prefix = minetest.colorize("#FFFF00", "[Chess] ")
16 local letters = {'A','B','C','D','E','F','G','H'}
17
18 local pieces = {
19         "realchess:rook_black_1",
20         "realchess:knight_black_1",
21         "realchess:bishop_black_1",
22         "realchess:queen_black",
23         "realchess:king_black",
24         "realchess:bishop_black_2",
25         "realchess:knight_black_2",
26         "realchess:rook_black_2",
27         "realchess:pawn_black_1",
28         "realchess:pawn_black_2",
29         "realchess:pawn_black_3",
30         "realchess:pawn_black_4",
31         "realchess:pawn_black_5",
32         "realchess:pawn_black_6",
33         "realchess:pawn_black_7",
34         "realchess:pawn_black_8",
35         '','','','','','','','','','','','','','','','',
36         '','','','','','','','','','','','','','','','',
37         "realchess:pawn_white_1",
38         "realchess:pawn_white_2",
39         "realchess:pawn_white_3",
40         "realchess:pawn_white_4",
41         "realchess:pawn_white_5",
42         "realchess:pawn_white_6",
43         "realchess:pawn_white_7",
44         "realchess:pawn_white_8",
45         "realchess:rook_white_1",
46         "realchess:knight_white_1",
47         "realchess:bishop_white_1",
48         "realchess:queen_white",
49         "realchess:king_white",
50         "realchess:bishop_white_2",
51         "realchess:knight_white_2",
52         "realchess:rook_white_2"
53 }
54
55 local pieces_str, x = "", 0
56 for i = 1, #pieces do
57         local p = pieces[i]:match(":(%w+_%w+)")
58         if pieces[i]:find(":(%w+)_(%w+)") and not pieces_str:find(p) then
59                 pieces_str = pieces_str .. x .. "=" .. p .. ".png,"
60                 x = x + 1
61         end
62 end
63 pieces_str = pieces_str .. "69=mailbox_blank16.png"
64
65 local fs = [[
66         size[14.7,10;]
67         no_prepend[]
68         bgcolor[#080808BB;true]
69         background[0,0;14.7,10;chess_bg.png]
70         list[context;board;0.3,1;8,8;]
71         listcolors[#00000000;#00000000;#00000000;#30434C;#FFF]
72         tableoptions[background=#00000000;highlight=#00000000;border=false]
73         button[10.5,8.5;2,2;new;New game]
74 ]] ..  "tablecolumns[image," .. pieces_str ..
75                 ";text;color;text;color;text;image," .. pieces_str .. "]"
76
77 function realchess.init(pos)
78         local meta = minetest.get_meta(pos)
79         local inv = meta:get_inventory()
80
81         meta:set_string("formspec", fs)
82         meta:set_string("infotext", "Chess Board")
83         meta:set_string("playerBlack", "")
84         meta:set_string("playerWhite", "")
85         meta:set_string("lastMove", "")
86         meta:set_string("winner", "")
87
88         meta:set_int("lastMoveTime", 0)
89         meta:set_int("castlingBlackL", 1)
90         meta:set_int("castlingBlackR", 1)
91         meta:set_int("castlingWhiteL", 1)
92         meta:set_int("castlingWhiteR", 1)
93
94         meta:set_string("moves", "")
95         meta:set_string("eaten", "")
96
97         inv:set_list("board", pieces)
98         inv:set_size("board", 64)
99 end
100
101 function realchess.move(pos, from_list, from_index, to_list, to_index, _, player)
102         if from_list ~= "board" and to_list ~= "board" then
103                 return 0
104         end
105
106         local playerName = player:get_player_name()
107         local meta = minetest.get_meta(pos)
108
109         if meta:get_string("winner") ~= "" then
110                 return 0
111         end
112
113         local inv = meta:get_inventory()
114         local pieceFrom = inv:get_stack(from_list, from_index):get_name()
115         local pieceTo = inv:get_stack(to_list, to_index):get_name()
116         local lastMove = meta:get_string("lastMove")
117         local thisMove -- will replace lastMove when move is legal
118         local playerWhite = meta:get_string("playerWhite")
119         local playerBlack = meta:get_string("playerBlack")
120
121         if pieceFrom:find("white") then
122                 if playerWhite ~= "" and playerWhite ~= playerName then
123                         minetest.chat_send_player(playerName, chat_prefix .. "Someone else plays white pieces!")
124                         return 0
125                 end
126                 if lastMove ~= "" and lastMove ~= "black" then
127                         return 0
128                 end
129                 if pieceTo:find("white") then
130                         -- Don't replace pieces of same color
131                         return 0
132                 end
133                 playerWhite = playerName
134                 thisMove = "white"
135         elseif pieceFrom:find("black") then
136                 if playerBlack ~= "" and playerBlack ~= playerName then
137                         minetest.chat_send_player(playerName, chat_prefix .. "Someone else plays black pieces!")
138                         return 0
139                 end
140                 if lastMove ~= "" and lastMove ~= "white" then
141                         return 0
142                 end
143                 if pieceTo:find("black") then
144                         -- Don't replace pieces of same color
145                         return 0
146                 end
147                 playerBlack = playerName
148                 thisMove = "black"
149         end
150
151         -- DETERMINISTIC MOVING
152
153         local from_x, from_y = index_to_xy(from_index)
154         local to_x, to_y = index_to_xy(to_index)
155
156         if pieceFrom:sub(11,14) == "pawn" then
157                 if thisMove == "white" then
158                         local pawnWhiteMove = inv:get_stack(from_list, xy_to_index(from_x, from_y - 1)):get_name()
159                         -- white pawns can go up only
160                         if from_y - 1 == to_y then
161                                 if from_x == to_x then
162                                         if pieceTo ~= "" then
163                                                 return 0
164                                         elseif to_index >= 1 and to_index <= 8 then
165                                                 inv:set_stack(from_list, from_index, "realchess:queen_white")
166                                         end
167                                 elseif from_x - 1 == to_x or from_x + 1 == to_x then
168                                         if not pieceTo:find("black") then
169                                                 return 0
170                                         elseif to_index >= 1 and to_index <= 8 then
171                                                 inv:set_stack(from_list, from_index, "realchess:queen_white")
172                                         end
173                                 else
174                                         return 0
175                                 end
176                         elseif from_y - 2 == to_y then
177                                 if pieceTo ~= "" or from_y < 6 or pawnWhiteMove ~= "" then
178                                         return 0
179                                 end
180                         else
181                                 return 0
182                         end
183
184                         -- if x not changed,
185                         --   ensure that destination cell is empty
186                         -- elseif x changed one unit left or right
187                         --   ensure the pawn is killing opponent piece
188                         -- else
189                         --   move is not legal - abort
190
191                         if from_x == to_x then
192                                 if pieceTo ~= "" then
193                                         return 0
194                                 end
195                         elseif from_x - 1 == to_x or from_x + 1 == to_x then
196                                 if not pieceTo:find("black") then
197                                         return 0
198                                 end
199                         else
200                                 return 0
201                         end
202
203                 elseif thisMove == "black" then
204                         local pawnBlackMove = inv:get_stack(from_list, xy_to_index(from_x, from_y + 1)):get_name()
205                         -- black pawns can go down only
206                         if from_y + 1 == to_y then
207                                 if from_x == to_x then
208                                         if pieceTo ~= "" then
209                                                 return 0
210                                         elseif to_index >= 57 and to_index <= 64 then
211                                                 inv:set_stack(from_list, from_index, "realchess:queen_black")
212                                         end
213                                 elseif from_x - 1 == to_x or from_x + 1 == to_x then
214                                         if not pieceTo:find("white") then
215                                                 return 0
216                                         elseif to_index >= 57 and to_index <= 64 then
217                                                 inv:set_stack(from_list, from_index, "realchess:queen_black")
218                                         end
219                                 else
220                                         return 0
221                                 end
222                         elseif from_y + 2 == to_y then
223                                 if pieceTo ~= "" or from_y > 1 or pawnBlackMove ~= "" then
224                                         return 0
225                                 end
226                         else
227                                 return 0
228                         end
229
230                         -- if x not changed,
231                         --   ensure that destination cell is empty
232                         -- elseif x changed one unit left or right
233                         --   ensure the pawn is killing opponent piece
234                         -- else
235                         --   move is not legal - abort
236
237                         if from_x == to_x then
238                                 if pieceTo ~= "" then
239                                         return 0
240                                 end
241                         elseif from_x - 1 == to_x or from_x + 1 == to_x then
242                                 if not pieceTo:find("white") then
243                                         return 0
244                                 end
245                         else
246                                 return 0
247                         end
248                 else
249                         return 0
250                 end
251
252         elseif pieceFrom:sub(11,14) == "rook" then
253                 if from_x == to_x then
254                         -- moving vertically
255                         if from_y < to_y then
256                                 -- moving down
257                                 -- ensure that no piece disturbs the way
258                                 for i = from_y + 1, to_y - 1 do
259                                         if inv:get_stack(from_list, xy_to_index(from_x, i)):get_name() ~= "" then
260                                                 return 0
261                                         end
262                                 end
263                         else
264                                 -- mocing up
265                                 -- ensure that no piece disturbs the way
266                                 for i = to_y + 1, from_y - 1 do
267                                         if inv:get_stack(from_list, xy_to_index(from_x, i)):get_name() ~= "" then
268                                                 return 0
269                                         end
270                                 end
271                         end
272                 elseif from_y == to_y then
273                         -- mocing horizontally
274                         if from_x < to_x then
275                                 -- mocing right
276                                 -- ensure that no piece disturbs the way
277                                 for i = from_x + 1, to_x - 1 do
278                                         if inv:get_stack(from_list, xy_to_index(i, from_y)):get_name() ~= "" then
279                                                 return 0
280                                         end
281                                 end
282                         else
283                                 -- mocing left
284                                 -- ensure that no piece disturbs the way
285                                 for i = to_x + 1, from_x - 1 do
286                                         if inv:get_stack(from_list, xy_to_index(i, from_y)):get_name() ~= "" then
287                                                 return 0
288                                         end
289                                 end
290                         end
291                 else
292                         -- attempt to move arbitrarily -> abort
293                         return 0
294                 end
295
296                 if thisMove == "white" or thisMove == "black" then
297                         if pieceFrom:sub(-1) == "1" then
298                                 meta:set_int("castlingWhiteL", 0)
299                         elseif pieceFrom:sub(-1) == "2" then
300                                 meta:set_int("castlingWhiteR", 0)
301                         end
302                 end
303
304         elseif pieceFrom:sub(11,16) == "knight" then
305                 -- get relative pos
306                 local dx = from_x - to_x
307                 local dy = from_y - to_y
308
309                 -- get absolute values
310                 if dx < 0 then dx = -dx end
311                 if dy < 0 then dy = -dy end
312
313                 -- sort x and y
314                 if dx > dy then dx, dy = dy, dx end
315
316                 -- ensure that dx == 1 and dy == 2
317                 if dx ~= 1 or dy ~= 2 then
318                         return 0
319                 end
320                 -- just ensure that destination cell does not contain friend piece
321                 -- ^ it was done already thus everything ok
322
323         elseif pieceFrom:sub(11,16) == "bishop" then
324                 -- get relative pos
325                 local dx = from_x - to_x
326                 local dy = from_y - to_y
327
328                 -- get absolute values
329                 if dx < 0 then dx = -dx end
330                 if dy < 0 then dy = -dy end
331
332                 -- ensure dx and dy are equal
333                 if dx ~= dy then return 0 end
334
335                 if from_x < to_x then
336                         if from_y < to_y then
337                                 -- moving right-down
338                                 -- ensure that no piece disturbs the way
339                                 for i = 1, dx - 1 do
340                                         if inv:get_stack(from_list, xy_to_index(from_x + i, from_y + i)):get_name() ~= "" then
341                                                 return 0
342                                         end
343                                 end
344                         else
345                                 -- moving right-up
346                                 -- ensure that no piece disturbs the way
347                                 for i = 1, dx - 1 do
348                                         if inv:get_stack(from_list, xy_to_index(from_x + i, from_y - i)):get_name() ~= "" then
349                                                 return 0
350                                         end
351                                 end
352                         end
353                 else
354                         if from_y < to_y then
355                                 -- moving left-down
356                                 -- ensure that no piece disturbs the way
357                                 for i = 1, dx - 1 do
358                                         if inv:get_stack(from_list, xy_to_index(from_x - i, from_y + i)):get_name() ~= "" then
359                                                 return 0
360                                         end
361                                 end
362                         else
363                                 -- moving left-up
364                                 -- ensure that no piece disturbs the way
365                                 for i = 1, dx - 1 do
366                                         if inv:get_stack(from_list, xy_to_index(from_x - i, from_y - i)):get_name() ~= "" then
367                                                 return 0
368                                         end
369                                 end
370                         end
371                 end
372
373         elseif pieceFrom:sub(11,15) == "queen" then
374                 local dx = from_x - to_x
375                 local dy = from_y - to_y
376
377                 -- get absolute values
378                 if dx < 0 then dx = -dx end
379                 if dy < 0 then dy = -dy end
380
381                 -- ensure valid relative move
382                 if dx ~= 0 and dy ~= 0 and dx ~= dy then
383                         return 0
384                 end
385
386                 if from_x == to_x then
387                         if from_y < to_y then
388                                 -- goes down
389                                 -- ensure that no piece disturbs the way
390                                 for i = 1, dx - 1 do
391                                         if inv:get_stack(from_list, xy_to_index(from_x, from_y + i)):get_name() ~= "" then
392                                                 return 0
393                                         end
394                                 end
395                         else
396                                 -- goes up
397                                 -- ensure that no piece disturbs the way
398                                 for i = 1, dx - 1 do
399                                         if inv:get_stack(from_list, xy_to_index(from_x, from_y - i)):get_name() ~= "" then
400                                                 return 0
401                                         end
402                                 end
403                         end
404                 elseif from_x < to_x then
405                         if from_y == to_y then
406                                 -- goes right
407                                 -- ensure that no piece disturbs the way
408                                 for i = 1, dx - 1 do
409                                         if inv:get_stack(from_list, xy_to_index(from_x + i, from_y)):get_name() ~= "" then
410                                                 return 0
411                                         end
412                                 end
413                         elseif from_y < to_y then
414                                 -- goes right-down
415                                 -- ensure that no piece disturbs the way
416                                 for i = 1, dx - 1 do
417                                         if inv:get_stack(from_list, xy_to_index(from_x + i, from_y + i)):get_name() ~= "" then
418                                                 return 0
419                                         end
420                                 end
421                         else
422                                 -- goes right-up
423                                 -- ensure that no piece disturbs the way
424                                 for i = 1, dx - 1 do
425                                         if inv:get_stack(from_list, xy_to_index(from_x + i, from_y - i)):get_name() ~= "" then
426                                                 return 0
427                                         end
428                                 end
429                         end
430                 else
431                         if from_y == to_y then
432                                 -- goes left
433                                 -- ensure that no piece disturbs the way and destination cell does
434                                 for i = 1, dx - 1 do
435                                         if inv:get_stack(from_list, xy_to_index(from_x - i, from_y)):get_name() ~= "" then
436                                                 return 0
437                                         end
438                                 end
439                         elseif from_y < to_y then
440                                 -- goes left-down
441                                 -- ensure that no piece disturbs the way
442                                 for i = 1, dx - 1 do
443                                         if inv:get_stack(from_list, xy_to_index(from_x - i, from_y + i)):get_name() ~= "" then
444                                                 return 0
445                                         end
446                                 end
447                         else
448                                 -- goes left-up
449                                 -- ensure that no piece disturbs the way
450                                 for i = 1, dx - 1 do
451                                         if inv:get_stack(from_list, xy_to_index(from_x - i, from_y - i)):get_name() ~= "" then
452                                                 return 0
453                                         end
454                                 end
455                         end
456                 end
457
458         elseif pieceFrom:sub(11,14) == "king" then
459                 local dx = from_x - to_x
460                 local dy = from_y - to_y
461                 local check = true
462
463                 if thisMove == "white" then
464                         if from_y == 7 and to_y == 7 then
465                                 if to_x == 1 then
466                                         local castlingWhiteL = meta:get_int("castlingWhiteL")
467                                         local idx57 = inv:get_stack(from_list, 57):get_name()
468
469                                         if castlingWhiteL == 1 and idx57 == "realchess:rook_white_1" then
470                                                 for i = 58, from_index - 1 do
471                                                         if inv:get_stack(from_list, i):get_name() ~= "" then
472                                                                 return 0
473                                                         end
474                                                 end
475                                                 inv:set_stack(from_list, 57, "")
476                                                 inv:set_stack(from_list, 59, "realchess:rook_white_1")
477                                                 check = false
478                                         end
479                                 elseif to_x == 6 then
480                                         local castlingWhiteR = meta:get_int("castlingWhiteR")
481                                         local idx64 = inv:get_stack(from_list, 64):get_name()
482
483                                         if castlingWhiteR == 1 and idx64 == "realchess:rook_white_2" then
484                                                 for i = from_index + 1, 63 do
485                                                         if inv:get_stack(from_list, i):get_name() ~= "" then
486                                                                 return 0
487                                                         end
488                                                 end
489                                                 inv:set_stack(from_list, 62, "realchess:rook_white_2")
490                                                 inv:set_stack(from_list, 64, "")
491                                                 check = false
492                                         end
493                                 end
494                         end
495                 elseif thisMove == "black" then
496                         if from_y == 0 and to_y == 0 then
497                                 if to_x == 1 then
498                                         local castlingBlackL = meta:get_int("castlingBlackL")
499                                         local idx1 = inv:get_stack(from_list, 1):get_name()
500
501                                         if castlingBlackL == 1 and idx1 == "realchess:rook_black_1" then
502                                                 for i = 2, from_index - 1 do
503                                                         if inv:get_stack(from_list, i):get_name() ~= "" then
504                                                                 return 0
505                                                         end
506                                                 end
507                                                 inv:set_stack(from_list, 1, "")
508                                                 inv:set_stack(from_list, 3, "realchess:rook_black_1")
509                                                 check = false
510                                         end
511                                 elseif to_x == 6 then
512                                         local castlingBlackR = meta:get_int("castlingBlackR")
513                                         local idx8 = inv:get_stack(from_list, 1):get_name()
514
515                                         if castlingBlackR == 1 and idx8 == "realchess:rook_black_2" then
516                                                 for i = from_index + 1, 7 do
517                                                         if inv:get_stack(from_list, i):get_name() ~= "" then
518                                                                 return 0
519                                                         end
520                                                 end
521                                                 inv:set_stack(from_list, 6, "realchess:rook_black_2")
522                                                 inv:set_stack(from_list, 8, "")
523                                                 check = false
524                                         end
525                                 end
526                         end
527                 end
528
529                 if check then
530                         if dx < 0 then dx = -dx end
531                         if dy < 0 then dy = -dy end
532                         if dx > 1 or dy > 1 then return 0 end
533                 end
534
535                 if thisMove == "white" then
536                         meta:set_int("castlingWhiteL", 0)
537                         meta:set_int("castlingWhiteR", 0)
538                 elseif thisMove == "black" then
539                         meta:set_int("castlingBlackL", 0)
540                         meta:set_int("castlingBlackR", 0)
541                 end
542         end
543
544         meta:set_string("playerWhite", playerWhite)
545         meta:set_string("playerBlack", playerBlack)
546         lastMove = thisMove
547         meta:set_string("lastMove", lastMove)
548         meta:set_int("lastMoveTime", minetest.get_gametime())
549
550         if pieceTo:sub(11,14) == "king" then
551                 meta:set_string("winner", thisMove)
552         end
553
554         local moves = meta:get_string("moves")
555         local pieceFrom_s = pieceFrom:match(":(%w+_%w+)")
556         local pieceFrom_si_id = pieces_str:match("(%d+)=" .. pieceFrom_s)
557         local pieceTo_s = pieceTo_s ~= "" and pieceTo:match(":(%w+_%w+)") or ""
558         local pieceTo_si_id = pieceTo_s ~= "" and pieces_str:match("(%d+)=" .. pieceTo_s) or ""
559
560         moves = pieceFrom_si_id .. "," ..
561                 letters[from_x + 1] .. (from_y + 1) .. "," ..
562                         (pieceTo ~= "" and "#33FF33" or "#FFFFFF") .. ", > ,#FFFFFF," ..
563                 letters[to_x + 1] .. (to_y + 1) .. "," ..
564                 (pieceTo ~= "" and pieceTo_si_id or "69") .. "," ..
565                 moves
566
567         meta:set_string("moves", moves)
568
569         local eaten = meta:get_string("eaten")
570         if pieceTo ~= "" then
571                 eaten = eaten .. pieceTo_s .. ","
572         end
573
574         meta:set_string("eaten", eaten)
575
576         local eaten_t = string.split(eaten, ",")
577         local eaten_img = ""
578
579         local a, b = 0, 0
580         for i = 1, #eaten_t do
581                 local is_white = eaten_t[i]:sub(-5,-1) == "white"
582                 local X = (is_white and a or b) % 4
583                 local Y = ((is_white and a or b) % 16 - X) / 4
584
585                 if is_white then
586                         a = a + 1
587                 else
588                         b = b + 1
589                 end
590
591                 eaten_img = eaten_img ..
592                         "image[" .. ((X + (is_white and 11.7 or 8.8)) - (X * 0.45)) .. "," ..
593                                     ((Y + 5.56) - (Y * 0.2)) .. ";1,1;" .. eaten_t[i] .. ".png]"
594         end
595
596         local black_win = lastMove == "black" and pieceTo:sub(11,14) == "king"
597         local white_win = lastMove == "white" and pieceTo:sub(11,14) == "king"
598
599         local formspec = fs ..
600                 "label[2,0.3;" .. (black_win and
601                                    minetest.colorize("#00FF00", playerBlack .. " has win") or
602                                    minetest.colorize("#000001",
603                                         (lastMove == "white" and playerBlack ~= "" and not white_win) and
604                                          playerBlack .. "..." or playerBlack)) .. "]" ..
605                 "label[2,9.15;" .. (white_win and
606                                     minetest.colorize("#00FF00", playerWhite .. " has win") or
607                                     minetest.colorize("#000001",
608                                         (lastMove == "black" and playerWhite ~= "" and not black_win) and
609                                          playerWhite .. "..." or playerWhite)) .. "]" ..
610                 "table[8.9,1.05;5.07,3.75;moves;" .. moves:sub(1,-2) .. ";1]" ..
611                 eaten_img
612
613         meta:set_string("formspec", formspec)
614
615         return 1
616 end
617
618 local function timeout_format(timeout_limit)
619         local time_remaining = timeout_limit - minetest.get_gametime()
620         local minutes = math.floor(time_remaining / 60)
621         local seconds = time_remaining % 60
622
623         if minutes == 0 then
624                 return seconds .. " sec."
625         end
626
627         return minutes .. " min. " .. seconds .. " sec."
628 end
629
630 function realchess.fields(pos, _, fields, sender)
631         local playerName = sender:get_player_name()
632         local meta = minetest.get_meta(pos)
633         local timeout_limit = meta:get_int("lastMoveTime") + 300
634         local playerWhite = meta:get_string("playerWhite")
635         local playerBlack = meta:get_string("playerBlack")
636         local lastMoveTime = meta:get_int("lastMoveTime")
637         if fields.quit then return end
638
639         -- timeout is 5 min. by default for resetting the game (non-players only)
640         if fields.new then
641                 if (playerWhite == playerName or playerBlack == playerName) then
642                         realchess.init(pos)
643                 else
644                         minetest.chat_send_player(playerName, chat_prefix ..
645                                 "You can't reset the chessboard, a game has been started.\n" ..
646                                 "If you are not a current player, try again in " ..
647                                         timeout_format(timeout_limit))
648                 end
649         end
650
651         if fields.new and lastMoveTime ~= 0 and minetest.get_gametime() >= timeout_limit and
652                         (playerWhite ~= playerName or playerBlack ~= playerName) then
653                 realchess.init(pos)
654         end
655 end
656
657 function realchess.dig(pos, player)
658         if not player then
659                 return false
660         end
661
662         local meta = minetest.get_meta(pos)
663         local playerName = player:get_player_name()
664         local timeout_limit = meta:get_int("lastMoveTime") + 300
665         local lastMoveTime = meta:get_int("lastMoveTime")
666
667         -- timeout is 5 min. by default for digging the chessboard (non-players only)
668         return (lastMoveTime == 0 and minetest.get_gametime() > timeout_limit) or
669                 minetest.chat_send_player(playerName, chat_prefix ..
670                                 "You can't dig the chessboard, a game has been started.\n" ..
671                                 "Reset it first if you're a current player, or dig it again in " ..
672                                 timeout_format(timeout_limit))
673 end
674
675 function realchess.on_move(pos, from_list, from_index)
676         local inv = minetest.get_meta(pos):get_inventory()
677         inv:set_stack(from_list, from_index, '')
678         return false
679 end
680
681 minetest.register_node(":realchess:chessboard", {
682         description = "Chess Board",
683         drawtype = "nodebox",
684         paramtype = "light",
685         paramtype2 = "facedir",
686         inventory_image = "chessboard_top.png",
687         wield_image = "chessboard_top.png",
688         tiles = {"chessboard_top.png", "chessboard_top.png", "chessboard_sides.png"},
689         groups = {choppy=3, oddly_breakable_by_hand=2, flammable=3},
690         sounds = default.node_sound_wood_defaults(),
691         node_box = {type = "fixed", fixed = {-.375, -.5, -.375, .375, -.4375, .375}},
692         sunlight_propagates = true,
693         on_rotate = screwdriver.rotate_simple,
694         can_dig = realchess.dig,
695         on_construct = realchess.init,
696         on_receive_fields = realchess.fields,
697         allow_metadata_inventory_move = realchess.move,
698         on_metadata_inventory_move = realchess.on_move,
699         allow_metadata_inventory_take = function() return 0 end
700 })
701
702 local function register_piece(name, count)
703         for _, color in pairs({"black", "white"}) do
704         if not count then
705                 minetest.register_craftitem(":realchess:" .. name .. "_" .. color, {
706                         description = color:gsub("^%l", string.upper) .. " " .. name:gsub("^%l", string.upper),
707                         inventory_image = name .. "_" .. color .. ".png",
708                         stack_max = 1,
709                         groups = {not_in_creative_inventory=1}
710                 })
711         else
712                 for i = 1, count do
713                         minetest.register_craftitem(":realchess:" .. name .. "_" .. color .. "_" .. i, {
714                                 description = color:gsub("^%l", string.upper) .. " " .. name:gsub("^%l", string.upper),
715                                 inventory_image = name .. "_" .. color .. ".png",
716                                 stack_max = 1,
717                                 groups = {not_in_creative_inventory=1}
718                         })
719                 end
720         end
721         end
722 end
723
724 register_piece("pawn", 8)
725 register_piece("rook", 2)
726 register_piece("knight", 2)
727 register_piece("bishop", 2)
728 register_piece("queen")
729 register_piece("king")
730
731 -- Recipes
732
733 minetest.register_craft({
734         output = "realchess:chessboard",
735         recipe = {
736                 {"dye:black", "dye:white", "dye:black"},
737                 {"stairs:slab_wood", "stairs:slab_wood", "stairs:slab_wood"}
738         }
739 })