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