book.lua 4.6 KB

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