init.lua 24 KB

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