init.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. --[[
  2. More Blocks: circular saw
  3. Copyright (c) 2011-2015 Calinou and contributors.
  4. Licensed under the zlib license. See LICENSE.md for more information.
  5. --]]
  6. -- Localize for performance.
  7. local math_floor = math.floor
  8. local math_min = math.min
  9. local math_max = math.max
  10. local S = function(str) return str end
  11. if not minetest.global_exists("circular_saw") then circular_saw = {} end
  12. -- This is populated by stairsplus:register_all:
  13. circular_saw.known_nodes = circular_saw.known_nodes or {}
  14. -- 3rd parameter: how many microblocks does this shape cost:
  15. -- It may cause slight loss, but no gain.
  16. -- 1st and 2nd parameters are nodename prefix/postfixes.
  17. circular_saw.names = {
  18. {"micro", "_1", 1},
  19. {"micro", "_1s", 1},
  20. {"micro", "_1c", 1},
  21. {"micro", "_c", 1},
  22. {"panel", "_1", 1},
  23. {"micro", "_2", 1},
  24. {"panel", "_2", 1},
  25. {"micro", "_4", 1},
  26. {"panel", "_4", 1},
  27. {"micro", "", 1},
  28. {"panel", "", 2},
  29. {"micro", "_12", 2},
  30. {"panel", "_12", 2},
  31. {"micro", "_14", 3},
  32. {"panel", "_14", 2},
  33. {"micro", "_15", 4},
  34. {"panel", "_15", 2},
  35. {"micro", "_16", 4},
  36. {"micro", "_16s", 2},
  37. {"panel", "_16", 4},
  38. {"stair", "_outer", 5},
  39. {"stair", "", 6},
  40. {"stair", "_inner", 7},
  41. {"slab", "_1", 1},
  42. {"slab", "_2", 1},
  43. {"slab", "_quarter", 2},
  44. {"slab", "", 4},
  45. {"slab", "_three_quarter", 6},
  46. {"slab", "_14", 7},
  47. {"slab", "_15", 8},
  48. {"stair", "_half", 3},
  49. {"stair", "_right_half", 3},
  50. {"stair", "_alt_1", 1},
  51. {"stair", "_alt_2", 1},
  52. {"stair", "_alt_4", 2},
  53. {"stair", "_alt_5", 1},
  54. {"stair", "_alt_6", 2},
  55. {"stair", "_alt", 4},
  56. {"slope", "", 4},
  57. {"slope", "_half", 2},
  58. {"slope", "_half_raised", 6},
  59. {"slope", "_inner", 7},
  60. {"slope", "_inner_half", 3},
  61. {"slope", "_inner_half_raised", 7},
  62. {"slope", "_inner_cut", 7},
  63. {"slope", "_inner_cut2", 8},
  64. {"slope", "_inner_cut3", 8},
  65. {"slope", "_inner_cut4", 4},
  66. {"slope", "_inner_cut5", 4},
  67. {"slope", "_inner_cut6", 7},
  68. {"slope", "_inner_cut7", 8},
  69. {"slope", "_inner_cut_half", 4},
  70. {"slope", "_inner_cut_half_raised", 8},
  71. {"slope", "_outer", 3},
  72. {"slope", "_outer_half", 2},
  73. {"slope", "_outer_half_raised", 6},
  74. {"slope", "_outer_cut", 2},
  75. {"slope", "_outer_cut_half", 1},
  76. {"slope", "_outer_cut_half_raised", 3},
  77. {"slope", "_cut", 4},
  78. {"slope", "_xslope_quarter", 2},
  79. {"slope", "_xslope_quarter2", 2},
  80. {"slope", "_xslope_three_quarter", 6},
  81. {"slope", "_xslope_three_quarter_half", 4},
  82. {"slope", "_xslope_cut", 4},
  83. {"slope", "_xslope_slope", 1},
  84. {"slab", "_two_sides", 1},
  85. {"slab", "_three_sides", 2},
  86. {"slab", "_three_sides_u", 2},
  87. {"slab", "_four_sides", 3},
  88. {"slab", "_hole", 3},
  89. {"slab", "_two_opposite", 1},
  90. {"slab", "_pit", 3},
  91. {"slab", "_pit_half", 2},
  92. {"stair", "_half_1", 1},
  93. {"stair", "_right_half_1", 1},
  94. {"slope", "_xslope_peak", 4},
  95. {"slope", "_xslope_peak_half", 2},
  96. {"slope", "_lh", 2},
  97. {"slope", "_half_lh", 1},
  98. {"slope", "_half_raised_lh", 3},
  99. {"slope", "_xslope_slope_lh", 1},
  100. {"slope", "_xslope_peak_lh", 2},
  101. {"slope", "_xslope_peak_half_lh", 1},
  102. {"slope", "_rh", 2},
  103. {"slope", "_half_rh", 1},
  104. {"slope", "_half_raised_rh", 3},
  105. {"slope", "_xslope_slope_rh", 1},
  106. {"slab", "_hole_half", 2},
  107. {"slope", "_astair_1", 6},
  108. {"slope", "_astair_2", 5},
  109. {"slope", "_astair_3", 6},
  110. {"slope", "_astair_4", 6},
  111. {"slope", "_astair_5", 8},
  112. {"panel", "_pillar", 8},
  113. {"panel", "_pcend", 8},
  114. }
  115. function circular_saw:get_cost(inv, stackname)
  116. for i, item in pairs(inv:get_list("output")) do
  117. if item:get_name() == stackname then
  118. return circular_saw.names[i][3]
  119. end
  120. end
  121. end
  122. function circular_saw:get_output_inv(modname, material, amount, max)
  123. if (not max or max < 1 or max > 64) then max = 64 end
  124. local list = {}
  125. -- If there is nothing inside, display empty inventory:
  126. if amount < 1 then
  127. return list
  128. end
  129. for i = 1, #circular_saw.names do
  130. local t = circular_saw.names[i]
  131. local cost = t[3]
  132. local balance = math_min(math_floor(amount/cost), max)
  133. local nodename = modname .. ":" .. t[1] .. "_" .. material .. t[2]
  134. if minetest.registered_nodes[nodename] then
  135. list[#list + 1] = nodename .. " " .. balance
  136. end
  137. end
  138. return list
  139. end
  140. -- Reset empty circular_saw after last full block has been taken out
  141. -- (or the circular_saw has been placed the first time)
  142. -- Note: max_offered is not reset:
  143. function circular_saw:reset(pos)
  144. local meta = minetest.get_meta(pos)
  145. local inv = meta:get_inventory()
  146. local owner = meta:get_string("owner")
  147. local dname = rename.gpn(owner)
  148. inv:set_stack("input", 1, "")
  149. inv:set_stack("micro", 1, "")
  150. inv:set_list("output", {})
  151. meta:set_int("anz", 0)
  152. meta:set_string("infotext",
  153. S("Circular Saw is Empty (Owned by <%s>!)")
  154. :format(dname))
  155. end
  156. -- Player has taken something out of the box or placed something inside
  157. -- that amounts to 'amount' microblocks (full block count * 8):
  158. function circular_saw:update_inventory(pos, amount)
  159. local meta = minetest.get_meta(pos)
  160. local inv = meta:get_inventory()
  161. amount = meta:get_int("anz") + amount
  162. -- The material is recycled automatically
  163. inv:set_stack("recycle", 1, ItemStack())
  164. if amount < 1 then -- If the last block is taken out.
  165. --minetest.log('action', 'amount is zero!')
  166. self:reset(pos)
  167. return
  168. end
  169. local stack = inv:get_stack("input", 1)
  170. -- At least one "normal" block is necessary to see what kind of stairs are requested.
  171. if stack:is_empty() then
  172. --minetest.log('action', 'stack is empty!')
  173. -- Any microblocks not taken out yet are now lost.
  174. -- (covers material loss in the machine)
  175. self:reset(pos)
  176. return
  177. end
  178. local node_name = stack:get_name() or ""
  179. local name_parts = circular_saw.known_nodes[node_name] or ""
  180. local modname = name_parts[1] or ""
  181. local material = name_parts[2] or ""
  182. local input_count = math_floor(amount / 8)
  183. local input_item = node_name .. " " .. input_count
  184. -- Display as many full blocks as possible.
  185. --minetest.log('action', 'adding to input! ' .. input_item)
  186. inv:set_stack("input", 1, input_item)
  187. -- The stairnodes made of default nodes use moreblocks namespace, other mods keep own:
  188. if modname == "default" then
  189. modname = "circular_saw"
  190. end
  191. -- print("circular_saw set to " .. modname .. " : "
  192. -- .. material .. " with " .. (amount) .. " microblocks.")
  193. -- 0-7 microblocks may remain left-over:
  194. local leftover_item = modname .. ":micro_" .. material .. "_bottom " .. (amount % 8)
  195. inv:set_stack("micro", 1, leftover_item)
  196. -- Display:
  197. local noutlist = self:get_output_inv(modname, material, amount, meta:get_int("max_offered"))
  198. --minetest.log('circular_saw: old inv size: ' .. inv:get_size("output"))
  199. --minetest.log('circular_saw: new inv size: ' .. #noutlist)
  200. ------------------------------------------------------------------------------
  201. -- So the devs broke the engine again.
  202. --inv:set_list("output", noutlist)
  203. local output_size = inv:get_size("output")
  204. for k = 1, output_size do
  205. if noutlist[k] then
  206. inv:set_stack("output", k, noutlist[k])
  207. else
  208. inv:set_stack("output", k, ItemStack())
  209. end
  210. end
  211. ------------------------------------------------------------------------------
  212. -- Store how many microblocks are available:
  213. meta:set_int("anz", amount)
  214. local material_desc = node_name
  215. local def = minetest.registered_items[node_name]
  216. if def and def.description then
  217. material_desc = utility.get_short_desc(def.description)
  218. end
  219. local owner = meta:get_string("owner")
  220. local dname = rename.gpn(owner)
  221. meta:set_string("infotext",
  222. S("Circular Saw is Working on \"%s\" (Owned by <%s>!)")
  223. :format(material_desc, dname))
  224. end
  225. -- The amount of items offered per shape can be configured:
  226. function circular_saw.on_receive_fields(pos, formname, fields, sender)
  227. local meta = minetest.get_meta(pos)
  228. local max = tonumber(fields.max_offered)
  229. if max and max > 0 then
  230. meta:set_string("max_offered", max)
  231. -- Update to show the correct number of items:
  232. circular_saw:update_inventory(pos, 0)
  233. end
  234. end
  235. local function has_saw_privilege(meta, player)
  236. if not meta then return false end
  237. if not player then return false end
  238. if minetest.check_player_privs(player, "protection_bypass") then
  239. return true
  240. end
  241. local owner = (meta:get_string("owner") or "")
  242. if player:get_player_name() == owner then
  243. return true
  244. end
  245. return false
  246. end
  247. -- Moving the inventory of the circular_saw around is not allowed because it
  248. -- is a fictional inventory. Moving inventory around would be rather
  249. -- impractical and make things more difficult to calculate:
  250. function circular_saw.allow_metadata_inventory_move(
  251. pos, from_list, from_index, to_list, to_index, count, player)
  252. return 0
  253. end
  254. -- Only input- and recycle-slot are intended as input slots:
  255. function circular_saw.allow_metadata_inventory_put(pos, listname, index, stack, player)
  256. local meta = minetest.get_meta(pos)
  257. if not has_saw_privilege(meta, player) then return 0 end
  258. if listname == "fuel" then
  259. if stack:get_name() == "default:mese_crystal_fragment" then
  260. return stack:get_count()
  261. else
  262. return 0
  263. end
  264. end
  265. -- The player is not allowed to put something in there:
  266. if listname == "output" or listname == "micro" then
  267. return 0
  268. end
  269. local inv = meta:get_inventory()
  270. local stackname = stack:get_name()
  271. local count = stack:get_count()
  272. -- Only alow those items that are offered in the output inventory to be recycled:
  273. if listname == "recycle" then
  274. if not inv:contains_item("output", stackname) then
  275. return 0
  276. end
  277. local stackmax = stack:get_stack_max()
  278. local instack = inv:get_stack("input", 1)
  279. local microstack = inv:get_stack("micro", 1)
  280. local incount = instack:get_count()
  281. local incost = (incount * 8) + microstack:get_count()
  282. local maxcost = (stackmax * 8) + 7
  283. local cost = circular_saw:get_cost(inv, stackname)
  284. if (incost + cost) > maxcost then
  285. return math_max((maxcost - incost) / cost, 0)
  286. end
  287. return count
  288. end
  289. -- Only accept certain blocks as input which are known to be craftable into stairs:
  290. if listname == "input" then
  291. if not inv:is_empty("input") then
  292. if inv:get_stack("input", index):get_name() ~= stackname then
  293. return 0
  294. end
  295. end
  296. if not inv:is_empty("micro") then
  297. local microstackname = inv:get_stack("micro", 1):get_name():gsub("^.+:micro_", "", 1)
  298. local cutstackname = stackname:gsub("^.+:", "", 1)
  299. if microstackname ~= cutstackname then
  300. return 0
  301. end
  302. end
  303. for name, t in pairs(circular_saw.known_nodes) do
  304. if name == stackname and inv:room_for_item("input", stack) then
  305. return count
  306. end
  307. end
  308. return 0
  309. end
  310. end
  311. -- Taking is allowed from all slots (even the internal microblock slot).
  312. -- Putting something in is slightly more complicated than taking anything
  313. -- because we have to make sure it is of a suitable material:
  314. function circular_saw.on_metadata_inventory_put(
  315. pos, listname, index, stack, player)
  316. -- We need to find out if the circular_saw is already set to a
  317. -- specific material or not:
  318. local meta = minetest.get_meta(pos)
  319. local inv = meta:get_inventory()
  320. local stackname = stack:get_name()
  321. local count = stack:get_count()
  322. -- Putting something into the input slot is only possible if that had
  323. -- been empty before or did contain something of the same material:
  324. if listname == "input" then
  325. -- Each new block is worth 8 microblocks:
  326. circular_saw:update_inventory(pos, 8 * count)
  327. elseif listname == "recycle" then
  328. -- Lets look which shape this represents:
  329. local cost = circular_saw:get_cost(inv, stackname)
  330. local input_stack = inv:get_stack("input", 1)
  331. -- check if this would not exceed input itemstack max_stacks
  332. if input_stack:get_count() + ((cost * count) / 8) <= input_stack:get_stack_max() then
  333. circular_saw:update_inventory(pos, cost * count)
  334. end
  335. end
  336. end
  337. local mese_to_cut_ratio = 32
  338. function circular_saw.allow_metadata_inventory_take(pos, listname, index, stack, player)
  339. local meta = minetest.get_meta(pos)
  340. if not has_saw_privilege(meta, player) then return 0 end
  341. if listname == "output" then
  342. local inv = meta:get_inventory()
  343. if inv:is_empty("fuel") then
  344. minetest.chat_send_player(player:get_player_name(), "# Server: No power to saw!")
  345. easyvend.sound_error(player:get_player_name())
  346. return 0
  347. else
  348. -- We do know how much each block at each position costs:
  349. local cost = circular_saw.names[index][3] * stack:get_count()
  350. local fuel = math.ceil(cost / mese_to_cut_ratio)
  351. if fuel < 1 then fuel = 1 end
  352. local fstack = inv:get_stack("fuel", 1)
  353. if fstack:get_count() < fuel then
  354. minetest.chat_send_player(player:get_player_name(), "# Server: Not enough energy!")
  355. easyvend.sound_error(player:get_player_name())
  356. return 0
  357. end
  358. end
  359. end
  360. return stack:get_count()
  361. end
  362. function circular_saw.on_metadata_inventory_take(
  363. pos, listname, index, stack, player)
  364. -- Prevent (inbuilt) swapping between inventories with different blocks
  365. -- corrupting player inventory or Saw with 'unknown' items.
  366. local meta = minetest.get_meta(pos)
  367. local inv = meta:get_inventory()
  368. local input_stack = inv:get_stack(listname, index)
  369. if not input_stack:is_empty() and input_stack:get_name()~=stack:get_name() then
  370. local player_inv = player:get_inventory()
  371. if player_inv:room_for_item("main", input_stack) then
  372. player_inv:add_item("main", input_stack)
  373. end
  374. circular_saw:reset(pos)
  375. return
  376. end
  377. -- If it is one of the offered stairs: find out how many
  378. -- microblocks have to be substracted:
  379. if listname == "output" then
  380. -- We do know how much each block at each position costs:
  381. local cost = circular_saw.names[index][3] * stack:get_count()
  382. local fuel = math.ceil(cost / mese_to_cut_ratio)
  383. if fuel < 1 then fuel = 1 end
  384. inv:remove_item("fuel", ItemStack("default:mese_crystal_fragment " .. fuel))
  385. circular_saw:update_inventory(pos, -cost)
  386. elseif listname == "micro" then
  387. -- Each microblock costs 1 microblock:
  388. circular_saw:update_inventory(pos, -stack:get_count())
  389. elseif listname == "input" then
  390. -- Each normal (= full) block taken costs 8 microblocks:
  391. circular_saw:update_inventory(pos, 8 * -stack:get_count())
  392. end
  393. -- The recycle field plays no role here since it is processed immediately.
  394. end
  395. function circular_saw.on_construct(pos)
  396. local meta = minetest.get_meta(pos)
  397. local fancy_inv = default.gui_bg..default.gui_bg_img..default.gui_slots
  398. -- Modify formspec size and inventory size in order to make room for more blocks.
  399. meta:set_string("formspec", "size[16,10]"..fancy_inv..
  400. "label[0,0;" ..S("Input\nMaterial").. "]" ..
  401. "list[context;input;1.5,0;1,1;]" ..
  402. "label[0,1;" ..S("Left-Over").. "]" ..
  403. "list[context;micro;1.5,1;1,1;]" ..
  404. "label[0,2;" ..S("Recycle\nOutput").. "]" ..
  405. "list[context;recycle;1.5,2;1,1;]" ..
  406. "field[0.3,4.0;1,1;max_offered;" ..S("Max").. ":;${max_offered}]" ..
  407. "button[1,3.7;1,1;Set;" ..S("Set").. "]" ..
  408. "list[context;output;2.8,0;13,6;]" ..
  409. "list[context;output;8.8,6;7,4;78]" ..
  410. "list[current_player;main;0.5,6.25;8,4;]" ..
  411. "label[0,5;Mese Fuel\nStorage]" ..
  412. "list[context;fuel;1.5,5;1,1;]"
  413. )
  414. meta:set_int("anz", 0) -- No microblocks inside yet.
  415. meta:set_string("max_offered", 64) -- How many items of this kind are offered by default?
  416. meta:set_string("infotext", S("Circular Saw is Empty"))
  417. local inv = meta:get_inventory()
  418. inv:set_size("input", 1) -- Input slot for full blocks of material x.
  419. inv:set_size("micro", 1) -- Storage for 1-7 surplus microblocks.
  420. inv:set_size("recycle", 1) -- Surplus partial blocks can be placed here.
  421. inv:set_size("output", 6*13+7*4) -- Many versions of stair-parts of material x.
  422. inv:set_size("fuel", 1)
  423. circular_saw:reset(pos)
  424. end
  425. function circular_saw.can_dig(pos,player)
  426. local meta = minetest.get_meta(pos)
  427. local inv = meta:get_inventory()
  428. if not inv:is_empty("input") or
  429. not inv:is_empty("micro") or
  430. not inv:is_empty("recycle") or
  431. not inv:is_empty("fuel") then
  432. return false
  433. end
  434. -- Can be dug by anyone when empty, not only by the owner:
  435. return true
  436. end
  437. minetest.register_node("circular_saw:circular_saw", {
  438. description = "Circular Table Saw\n\nRequires mese fragments to power the saw wheel.\nDo not allow children to use.",
  439. drawtype = "nodebox",
  440. node_box = {
  441. type = "fixed",
  442. fixed = {
  443. {-0.4, -0.5, -0.4, -0.25, 0.25, -0.25}, -- Leg
  444. {0.25, -0.5, 0.25, 0.4, 0.25, 0.4}, -- Leg
  445. {-0.4, -0.5, 0.25, -0.25, 0.25, 0.4}, -- Leg
  446. {0.25, -0.5, -0.4, 0.4, 0.25, -0.25}, -- Leg
  447. {-0.5, 0.25, -0.5, 0.5, 0.375, 0.5}, -- Tabletop
  448. {-0.01, 0.4375, -0.125, 0.01, 0.5, 0.125}, -- Saw blade (top)
  449. {-0.01, 0.375, -0.1875, 0.01, 0.4375, 0.1875}, -- Saw blade (bottom)
  450. {-0.25, -0.0625, -0.25, 0.25, 0.25, 0.25}, -- Motor case
  451. },
  452. },
  453. dumpnodes_tile = {"default_wood.png"},
  454. tiles = {
  455. "moreblocks_circular_saw_top.png",
  456. "moreblocks_circular_saw_bottom.png",
  457. "moreblocks_circular_saw_side.png"
  458. },
  459. paramtype = "light",
  460. sunlight_propagates = true,
  461. paramtype2 = "facedir",
  462. on_rotate = function(...)
  463. return screwdriver.rotate_simple(...)
  464. end,
  465. groups = utility.dig_groups("furniture", {
  466. immovable = 1,
  467. }),
  468. sounds = default.node_sound_wood_defaults(),
  469. on_construct = circular_saw.on_construct,
  470. can_dig = circular_saw.can_dig,
  471. stack_max = 1,
  472. -- Set the owner of this circular saw.
  473. after_place_node = function(pos, placer)
  474. local meta = minetest.get_meta(pos)
  475. local owner = placer and placer:get_player_name() or ""
  476. local dname = rename.gpn(owner)
  477. meta:set_string("owner", owner)
  478. meta:set_string("rename", dname)
  479. meta:set_string("infotext",
  480. S("Circular Saw is Empty (Owned by <%s>!)")
  481. :format(dname))
  482. end,
  483. -- The amount of items offered per shape can be configured:
  484. on_receive_fields = circular_saw.on_receive_fields,
  485. allow_metadata_inventory_move = circular_saw.allow_metadata_inventory_move,
  486. -- Only input- and recycle-slot are intended as input slots:
  487. allow_metadata_inventory_put = circular_saw.allow_metadata_inventory_put,
  488. allow_metadata_inventory_take = circular_saw.allow_metadata_inventory_take,
  489. -- Taking is allowed from all slots (even the internal microblock slot). Moving is forbidden.
  490. -- Putting something in is slightly more complicated than taking anything because we have to make sure it is of a suitable material:
  491. on_metadata_inventory_put = circular_saw.on_metadata_inventory_put,
  492. on_metadata_inventory_take = circular_saw.on_metadata_inventory_take,
  493. -- Called by rename LBM.
  494. _on_update_infotext = function(pos)
  495. local meta = minetest.get_meta(pos)
  496. local owner = meta:get_string("owner")
  497. -- Nobody placed this block.
  498. if owner == "" then
  499. return
  500. end
  501. local dname = rename.gpn(owner)
  502. meta:set_string("rename", dname)
  503. end,
  504. _on_update_formspec = function(pos)
  505. -- Update circular saw.
  506. circular_saw:update_inventory(pos, 0)
  507. end,
  508. })
  509. minetest.register_craft({
  510. output = "circular_saw:circular_saw",
  511. recipe = {
  512. {'', 'gem_cutter:blade', ''},
  513. {'group:wood', 'group:wood', 'group:wood'},
  514. {'cast_iron:ingot', 'techcrafts:electric_motor', 'cast_iron:ingot'},
  515. }
  516. })