book.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. local MAX_TEXT_SIZE = 10000
  2. local MAX_TITLE_SIZE = 80
  3. local SHORT_TITLE_SIZE = 35
  4. books.MAX_TEXT_SIZE = MAX_TEXT_SIZE
  5. books.MAX_TITLE_SIZE = MAX_TITLE_SIZE
  6. books.SHORT_TITLE_SIZE = SHORT_TITLE_SIZE
  7. local lpp = 14 -- Lines per book's page
  8. function books.book_on_use(itemstack, user)
  9. local player_name = user:get_player_name()
  10. local pos = user:get_pos()
  11. local meta = itemstack:get_meta()
  12. local title, text, owner = "", "", player_name
  13. local page, page_max, lines, string = 1, 1, {}, ""
  14. -- Backwards compatibility
  15. local old_data = minetest.deserialize(itemstack:get_metadata())
  16. if old_data then
  17. meta:from_table({ fields = old_data })
  18. end
  19. local data = meta:to_table().fields
  20. if data.owner then
  21. title = data.title
  22. text = data.text
  23. owner = data.owner
  24. for str in (text .. "\n"):gmatch("([^\n]*)[\n]") do
  25. lines[#lines+1] = str
  26. end
  27. if data.page then
  28. page = data.page
  29. page_max = data.page_max
  30. for i = ((lpp * page) - lpp) + 1, lpp * page do
  31. if not lines[i] then break end
  32. string = string .. lines[i] .. "\n"
  33. end
  34. end
  35. end
  36. local formspec
  37. if owner == player_name then
  38. formspec = "size[8,8.3]" .. default.gui_bg ..
  39. default.gui_bg_img ..
  40. "field[0.5,1;7.5,0;title;Title:;" ..
  41. minetest.formspec_escape(title) .. "]" ..
  42. "textarea[0.5,1.5;7.5,7;text;Contents:;" ..
  43. minetest.formspec_escape(text) .. "]" ..
  44. "button_exit[2.5,7.5;3,1;save;Save]"
  45. else
  46. formspec = "size[8,8.3]" .. default.gui_bg ..
  47. default.gui_bg_img ..
  48. "label[0.5,0.5;by <" .. rename.gpn(owner) .. ">]" ..
  49. "tablecolumns[color;text]" ..
  50. "tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
  51. "table[0.4,0;7,0.5;title;#FFFF00," .. minetest.formspec_escape(title) .. "]" ..
  52. "textarea[0.5,1.5;7.5,7;;" ..
  53. minetest.formspec_escape(string ~= "" and string or text) .. ";]" ..
  54. "button[2.4,7.6;0.8,0.8;book_prev;<]" ..
  55. "label[3.2,7.7;Page " .. page .. " of " .. page_max .. "]" ..
  56. "button[4.9,7.6;0.8,0.8;book_next;>]"
  57. end
  58. minetest.sound_play("book_open", {pos = pos, gain = 0.1, max_hear_distance = 16}, true)
  59. minetest.show_formspec(player_name, "books:book_formspec", formspec)
  60. return itemstack
  61. end
  62. books.on_player_receive_fields = function(player, formname, fields)
  63. if formname ~= "books:book_formspec" then return end
  64. local inv = player:get_inventory()
  65. local stack = player:get_wielded_item()
  66. local pos = player:get_pos()
  67. if fields.save and fields.title and fields.text
  68. and fields.title ~= "" and fields.text ~= "" then
  69. local new_stack, data
  70. if stack:get_name() ~= "books:book_written" then
  71. local count = stack:get_count()
  72. if count == 1 then
  73. stack:set_name("books:book_written")
  74. else
  75. stack:set_count(count - 1)
  76. new_stack = ItemStack("books:book_written")
  77. end
  78. else
  79. data = stack:get_meta():to_table().fields
  80. end
  81. if data and data.owner and data.owner ~= player:get_player_name() then
  82. return
  83. end
  84. if not data then data = {} end
  85. data.title = fields.title:sub(1, MAX_TITLE_SIZE)
  86. data.owner = player:get_player_name()
  87. local short_title = data.title
  88. -- Don't bother triming the title if the trailing dots would make it longer
  89. if #short_title > SHORT_TITLE_SIZE + 3 then
  90. short_title = short_title:sub(1, SHORT_TITLE_SIZE) .. "..."
  91. end
  92. data.description = "\"" .. short_title .. "\" By <"..rename.gpn(data.owner) .. ">"
  93. data.text = fields.text:sub(1, MAX_TEXT_SIZE)
  94. data.page = 1
  95. data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp)
  96. if new_stack then
  97. new_stack:get_meta():from_table({ fields = data })
  98. if inv:room_for_item("main", new_stack) then
  99. inv:add_item("main", new_stack)
  100. else
  101. minetest.add_item(player:get_pos(), new_stack)
  102. end
  103. else
  104. stack:get_meta():from_table({ fields = data })
  105. end
  106. minetest.sound_play("book_write", {pos = pos, gain = 0.1, max_hear_distance = 16}, true)
  107. elseif fields.book_next or fields.book_prev then
  108. local data = stack:get_meta():to_table().fields
  109. if not data or not data.page then
  110. return
  111. end
  112. data.page = tonumber(data.page)
  113. data.page_max = tonumber(data.page_max)
  114. if fields.book_next then
  115. data.page = data.page + 1
  116. if data.page > data.page_max then
  117. data.page = 1
  118. end
  119. else
  120. data.page = data.page - 1
  121. if data.page == 0 then
  122. data.page = data.page_max
  123. end
  124. end
  125. stack:get_meta():from_table({fields = data})
  126. stack = books.book_on_use(stack, player)
  127. minetest.sound_play("book_turn", {pos = pos, gain = 0.1, max_hear_distance = 16}, true)
  128. elseif fields.quit then
  129. minetest.sound_play("book_close", {pos = pos, gain = 0.1, max_hear_distance = 16}, true)
  130. end
  131. -- Update stack
  132. player:set_wielded_item(stack)
  133. end
  134. books.on_craft = function(itemstack, player, old_craft_grid, craft_inv)
  135. if itemstack:get_name() ~= "books:book_written" then
  136. return
  137. end
  138. local original
  139. local index
  140. for i = 1, player:get_inventory():get_size("craft") do
  141. if old_craft_grid[i]:get_name() == "books:book_written" then
  142. original = old_craft_grid[i]
  143. index = i
  144. end
  145. end
  146. if not original then
  147. return
  148. end
  149. local copymeta = original:get_meta():to_table()
  150. -- copy of the book held by player's mouse cursor
  151. itemstack:get_meta():from_table(copymeta)
  152. -- put the book with metadata back in the craft grid
  153. craft_inv:set_stack("craft", index, original)
  154. end