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