PageRenderTime 64ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/game/engine/util.lua

https://bitbucket.org/elcugo/t-engine
Lua | 2279 lines | 1708 code | 322 blank | 249 comment | 339 complexity | b00e943507c58742bf6669ea03578120 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. -- various stuff to make scripters life easier
  2. declare_global_constants {
  3. "approximate_distance",
  4. "bool_flag",
  5. "compass",
  6. "get_flag",
  7. "get_flag2",
  8. "get_func_from_flag",
  9. "has_flag",
  10. "inc_flag",
  11. "flag_inc",
  12. "known_flag",
  13. "flags_set",
  14. "flags_set_aux",
  15. "msg_format",
  16. "new_flag",
  17. "new_speed",
  18. "set_artifact_generation",
  19. "set_flag",
  20. "set_flags",
  21. "set_monster_generation",
  22. "set_object_generation",
  23. "stack_pop",
  24. "stack_push",
  25. "strcap",
  26. "bound",
  27. "choose_from_list",
  28. "choose_print_list",
  29. "book_capitals",
  30. "foreach_flags",
  31. "fold",
  32. "wiz_print",
  33. "convert_desc",
  34. "max",
  35. "min",
  36. "abs",
  37. "default_value",
  38. "new_key_action",
  39. "merge_tables",
  40. "unpack",
  41. "pack",
  42. "average_value",
  43. "iif",
  44. "table_copy",
  45. "cave_plain_floor",
  46. "define_as",
  47. "strjoin",
  48. "strsplit",
  49. "signed_number_tostring",
  50. "safe_new_flag",
  51. "clean_whitespace",
  52. "ends_with",
  53. "starts_with",
  54. "get_string",
  55. "generic_or_specific",
  56. "level_or_feat",
  57. "check_prevent_cmd",
  58. "player_can_see_bold",
  59. "who_to_faction",
  60. "notefind",
  61. "KTRL",
  62. "function_to_index",
  63. "index_to_function",
  64. "object_pickup_simple",
  65. "div_round",
  66. "display_colored_file",
  67. "switch",
  68. "permutate",
  69. "is_separate_word",
  70. "show_arr_as_file",
  71. "show_arr_as_list",
  72. "singularize",
  73. "pluralize",
  74. "add_special_plural",
  75. "add_regexp_plural",
  76. "display_list_simple",
  77. "line_breaks",
  78. "foreach_monster",
  79. "choose_from_list_simple",
  80. "or_msg",
  81. "randomize_list",
  82. "choose_multi_column",
  83. "monster_strsub",
  84. }
  85. declare_globals {
  86. "__hooks_list_callback",
  87. "__hooks_list_callback_max",
  88. "__lua_hook_heads",
  89. "__lua_hook_type_max",
  90. "__timers_callback_max",
  91. "__function_indexing",
  92. "__special_plurals",
  93. "__regexp_plurals",
  94. }
  95. -- Infinity: 2^31 - 2. "-2" so that both "infinity" and "-infinity" will
  96. -- fit into a 32 bit singed integer
  97. constant("infinity", 2147483646)
  98. -- Automatically defines a variable as an index
  99. function define_as(t, field)
  100. local def_as = t.define_as
  101. local val = t.__index__
  102. if field then
  103. def_as = t["define_"..field.."_as"]
  104. val = t[field]
  105. end
  106. if not t.load_script then t.load_script = "" end
  107. -- If not defined and not a table indexing, create the global variable
  108. if not globals()[def_as] and not strfind(def_as, '.', 1, 1) and not strfind(def_as, '[', 1, 1) then
  109. t.load_script = t.load_script..'globals()["'..def_as..'"] = '..val.."\n"
  110. else
  111. t.load_script = t.load_script..def_as.." = "..val.."\n"
  112. end
  113. end
  114. -- Better hook interface
  115. __hooks_list_callback = {}
  116. __hooks_list_callback_max = 0
  117. __lua_hook_type_max = hook.HARDCODED_MAX + 1
  118. __lua_hook_heads = {}
  119. -- Call as either:
  120. -- * hook(hook.FOO, function() ... end)
  121. -- * hook{ [hook.FOO] = function() ... end, [hook.FOO] = function() ... end, ...)
  122. function hook.__exec_module(a, b, c)
  123. local k, e
  124. -- Compute parameters
  125. local h_table, name_prefix
  126. if (type(a) == "table") then
  127. h_table = a
  128. name_prefix = b
  129. else
  130. h_table = { [a] = b }
  131. name_prefix = c
  132. end
  133. if not name_prefix then name_prefix = "" end
  134. for k, e in h_table do
  135. local hookname = "__"..name_prefix.."__hooks_list_callback"..__hooks_list_callback_max
  136. declare_global_constants{hookname}
  137. hook.lua_add_script(k, hookname, hookname)
  138. setglobal(hookname, e)
  139. __hooks_list_callback_max = __hooks_list_callback_max + 1
  140. end
  141. end
  142. function hook.lua_add_script(type, script, name)
  143. if type > hook.HARDCODED_MAX then
  144. local new_head = {}
  145. new_head.type = hook.HOOK_TYPE_LUA
  146. new_head.script = script
  147. new_head.name = name
  148. new_head.next = __lua_hook_heads[type]
  149. __lua_hook_heads[type] = new_head
  150. else
  151. hook.add_script(type, script, name)
  152. end
  153. end
  154. --- @fn
  155. -- @brief Add a new hook type to the hook.HOOK_NAME list
  156. -- @param name String the hook type's name
  157. function hook.new_hook_type(name)
  158. hook[name] = __lua_hook_type_max
  159. __lua_hook_type_max = __lua_hook_type_max + 1
  160. end
  161. --- @fn
  162. -- @brief Process a series of hooks(beware, ONLY lua hooks will be processed)
  163. -- @param type Number the hook to process
  164. -- @param ... Args the arguments to pass to the hooks
  165. function hook.process(type, ...)
  166. -- For all lua hooks
  167. local c
  168. if type > hook.HARDCODED_MAX then
  169. c = __lua_hook_heads[type]
  170. else
  171. c = hook.__get(type)
  172. end
  173. while c do
  174. if c.type == hook.HOOK_TYPE_C then
  175. error("Hook type "..type.. " with name "..c.name.." has a C callback, but is called by a lua process_hooks!")
  176. elseif c.type == hook.HOOK_TYPE_LUA then
  177. -- Get the function and call it
  178. local rets = pack(call(getglobal(c.script), arg))
  179. -- Done ?
  180. if rets[1] then return unpack(rets) end
  181. -- Restart is needed(because a hook was deleted) ?
  182. if hook.restart then
  183. c = hook.__get(type)
  184. hook.restart = nil
  185. else
  186. -- Ok try the next one
  187. c = c.next
  188. end
  189. else
  190. error("Unknown hook type "..c.type..", name "..c.name)
  191. end
  192. end
  193. return nil
  194. end
  195. -- Returns the direction of the compass that y2, x2 is from y, x
  196. -- the return value will be one of the following: north, south,
  197. -- east, west, north-east, south-east, south-west, north-west,
  198. -- or nil if it is within 2 tiles.
  199. function compass(y, x, y2, x2)
  200. local y_axis, x_axis, y_diff, x_diff, compass_dir
  201. -- is it close to the north/south meridian?
  202. y_diff = y2 - y
  203. -- determine if y2, x2 is to the north or south of y, x
  204. if (y_diff > -3) and (y_diff < 3) then
  205. y_axis = nil
  206. elseif y2 > y then
  207. y_axis = "south"
  208. else
  209. y_axis = "north"
  210. end
  211. -- is it close to the east/west meridian?
  212. x_diff = x2 - x
  213. -- determine if y2, x2 is to the east or west of y, x
  214. if (x_diff > -3) and (x_diff < 3) then
  215. x_axis = nil
  216. elseif x2 > x then
  217. x_axis = "east"
  218. else
  219. x_axis = "west"
  220. end
  221. -- Maybe it is (almost) due N/S
  222. if (not x_axis) and (not y_axis) then compass_dir = nil
  223. elseif not x_axis then compass_dir = y_axis
  224. -- Maybe it is (almost) due E/W
  225. elseif not y_axis then compass_dir = x_axis
  226. -- or if it is neither
  227. else compass_dir = y_axis.."-"..x_axis
  228. end
  229. return compass_dir
  230. end
  231. -- Returns an approximation of the 'distance' of y2, x2 from y, x.
  232. function approximate_distance(y, x, y2, x2)
  233. local y_diff, x_diff, most_dist, how_far
  234. -- how far to away to the north/south
  235. y_diff = y2 - y
  236. -- make sure it's a positive integer
  237. if y_diff < 0 then
  238. y_diff = 0 - y_diff
  239. end
  240. -- how far to away to the east/west
  241. x_diff = x2 - x
  242. -- make sure it's a positive integer
  243. if x_diff < 0 then
  244. x_diff = 0 - x_diff
  245. end
  246. -- find which one is the larger distance
  247. if x_diff > y_diff then
  248. most_dist = x_diff
  249. else
  250. most_dist = y_diff
  251. end
  252. -- how far away then?
  253. if most_dist >= 41 then
  254. how_far = "a long way"
  255. elseif most_dist >= 11 then
  256. how_far = "quite some way"
  257. else
  258. how_far = "not very far"
  259. end
  260. return how_far
  261. end
  262. -- better timer add function
  263. __timers_callback_max = 0
  264. function time.new_timer(t,a,b)
  265. assert(t.delay > 0, "no timer delay")
  266. assert(t.callback, "no timer callback")
  267. local timer
  268. if type(t.callback) == "function" then
  269. declare_global_constants("__timers_callback_"..__timers_callback_max)
  270. setglobal("__timers_callback_"..__timers_callback_max, t.callback)
  271. timer = time.__new_timer("__timers_callback_"..__timers_callback_max, t.delay)
  272. __timers_callback_max = __timers_callback_max + 1
  273. else
  274. timer = time.__new_timer(t.callback, t.delay)
  275. end
  276. timer.enabled = t.enabled
  277. return timer
  278. end
  279. function time.save_timer(name)
  280. add_loadsave(name..".enabled", nil)
  281. add_loadsave(name..".delay", 1)
  282. add_loadsave(name..".countdown", 1)
  283. end
  284. -- People can store their timers here
  285. time.timer = {}
  286. ------------------ RT timer ---------------
  287. time.rt = {}
  288. time.rt.__timers = {}
  289. time.rt.__timer_ids = {}
  290. function time.rt.exec(interval)
  291. if not time.rt.__timers[interval] then
  292. error("RT timer called with interval "..interval.." but no functions defined for this interval")
  293. return
  294. end
  295. for i = 1, getn(time.rt.__timers[interval]) do time.rt.__timers[interval][i](interval) end
  296. end
  297. function time.rt.add(interval, func)
  298. if not time.rt.__timers[interval] then
  299. time.rt.__timers[interval] = {}
  300. time.rt.__timer_ids[interval] = time.__rt_add(interval)
  301. end
  302. tinsert(time.rt.__timers[interval], func)
  303. end
  304. function time.rt.del(interval, func)
  305. if not time.rt.__timers[interval] then return nil end
  306. for i = 1, getn(time.rt.__timers[interval]) do
  307. if time.rt.__timers[interval][i] == func then
  308. tremove(time.rt.__timers[interval], i)
  309. break
  310. end
  311. end
  312. if getn(time.rt.__timers[interval]) == 0 then
  313. time.__rt_del(time.rt.__timer_ids[interval])
  314. time.rt.__timers[interval] = nil
  315. time.rt.__timer_ids[interval] = nil
  316. end
  317. end
  318. -- Easier access to special gene stuff
  319. function set_monster_generation(monster, state)
  320. if type(monster) == "string" then
  321. m_allow_special[test_monster_name(monster) + 1] = state
  322. else
  323. m_allow_special[monster + 1] = state
  324. end
  325. end
  326. function set_object_generation(obj, state)
  327. if type(obj) == "string" then
  328. m_allow_special[test_item_name(obj) + 1] = state
  329. else
  330. m_allow_special[obj + 1] = state
  331. end
  332. end
  333. function set_artifact_generation(obj, state)
  334. m_allow_special[obj + 1] = state
  335. end
  336. -- Flags
  337. -- Convenience function for multiple flag-setting at once
  338. function flags_set(flags, ...)
  339. flags_set_aux(flags,arg)
  340. end
  341. function flags_set_aux(flags,arg)
  342. for i = 1, getn(arg) do
  343. local flag, val
  344. if type(arg[i]) == "table" then
  345. flag = arg[i][1]
  346. val = arg[i][2]
  347. else
  348. flag = arg[i]
  349. val = 1
  350. end
  351. flag_set(flags, flag, val)
  352. end
  353. end
  354. -- Wrappers around above functions, assuming that the flags are in a member
  355. -- called 'flags' (a common convention)
  356. function has_flag(obj, flag)
  357. return flag_exists(obj.flags, flag)
  358. end
  359. function get_flag(obj, flag)
  360. return flag_get(obj.flags, flag)
  361. end
  362. function get_flag2(obj, flag)
  363. return flag_get2(obj.flags, flag)
  364. end
  365. function set_flag(obj, flag, v)
  366. flag_set(obj.flags, flag, v)
  367. end
  368. function set_flags(obj, ...)
  369. flags_set_aux(obj.flags, arg)
  370. end
  371. function bool_flag(obj, flag)
  372. flag_set(obj.flags, flag, true)
  373. end
  374. function inc_flag(obj, flag, v)
  375. flag_set(obj.flags, flag, v + flag_get(obj.flags, flag))
  376. end
  377. function known_flag(obj, flag)
  378. return flag_exists(obj.flags, flag) and flag_is_known(obj.flags, flag)
  379. end
  380. constant("__shared_flag", {})
  381. function new_flag(name, shared)
  382. local f = register_flag(name)
  383. declare_global_constants{"FLAG_"..name}
  384. setglobal("FLAG_"..name, f)
  385. if shared then
  386. __shared_flag[f] = true
  387. end
  388. return f
  389. end
  390. function flag_inc(obj, flag, v)
  391. local known = flag_is_known(obj, flag)
  392. flag_set(obj, flag, v + flag_get(obj, flag))
  393. flag_learn(obj, flag, known)
  394. end
  395. function new_speed(name, desc)
  396. local f = register_speed(name, desc)
  397. declare_global_constants{"SPEED_"..name}
  398. setglobal("SPEED_"..name, f)
  399. return f
  400. end
  401. -- Init the object tag
  402. do
  403. local a = new_object()
  404. local b = data_buffer_alloc()
  405. local c = monster_blow_new()
  406. local d = monster_type_new()
  407. local e = store_type_new()
  408. constant("TAG_MONSTER", tag(d))
  409. constant("TAG_OBJECT", tag(a))
  410. constant("TAG_STRING", tag(""))
  411. constant("TAG_TABLE", tag({}))
  412. constant("TAG_STORE", tag(e))
  413. constant("TAG_BUFFER", tag(b))
  414. constant("TAG_MONST_BLOW", tag(c))
  415. constant("TAG_TO_FLAG_TYPE",
  416. {
  417. [TAG_MONSTER] = { FLAG_TYPE_MONSTER, }
  418. [TAG_OBJECT] = { FLAG_TYPE_OBJ, }
  419. [TAG_STRING] = { FLAG_TYPE_STRING, "dup_string_to_void" }
  420. [TAG_STORE] = { FLAG_TYPE_STORE, }
  421. [TAG_BUFFER] = { FLAG_TYPE_DATA_BUFFER, "dup_buffer_to_void"}
  422. })
  423. constant("FLAG_TYPE_TO_PTR",
  424. {
  425. [FLAG_TYPE_MONSTER] = "to_monster_type"
  426. [FLAG_TYPE_OBJ] = "to_object"
  427. [FLAG_TYPE_STRING] = "to_string"
  428. [FLAG_TYPE_STORE] = "to_store_type"
  429. [FLAG_TYPE_DATA_BUFFER] = "to_buffer"
  430. })
  431. delete_object(a)
  432. data_buffer_delete(b)
  433. monster_blow_del(c)
  434. monster_type_free_memory(d)
  435. store_type_del(e)
  436. end
  437. -- Sets easy of use tag methods for flags
  438. -- get flag: flags[FLAG_FOO]
  439. -- set flag: flags[FLAG_FOO] = x
  440. -- set many flags: flags{ FOO=x BAR=y BAZ=z ... }
  441. -- del flag: flags[FLAG_FOO] = nil
  442. constant("TAG_FUNCTION", tag(function() end))
  443. constant("TAG_NUMBER", tag(0))
  444. constant("TAG_FLAG", tag(player.descriptors))
  445. global("old_flags_type_tags", {})
  446. old_flags_type_tags.gettable = settagmethod(tag(player.flags), "gettable",
  447. function(flags, f)
  448. if tag(f) == TAG_NUMBER then
  449. if flag_exists(flags, f) then
  450. local node = flag_get_node(flags, f)
  451. if (node.flags & FLAG_FLAG_FLAGS) ~= 0 then
  452. return flag_get_flags(flags, f)
  453. elseif (node.flags & FLAG_FLAG_PTR) ~= 0 then
  454. local typ = flag_node_get_ptr_type(node)
  455. return __wrap_ptr[FLAG_TYPE_TO_PTR[typ]](flag_get_ptr(flags, f, typ))
  456. else
  457. return flag_get(flags, f)
  458. end
  459. else
  460. return nil
  461. end
  462. else
  463. return old_flags_type_tags.gettable(flags, f)
  464. end
  465. end
  466. )
  467. old_flags_type_tags.settable = settagmethod(tag(player.flags), "settable",
  468. function(flags, f, v)
  469. if tag(f) == TAG_NUMBER then
  470. if v == nil then
  471. flag_remove(flags, f)
  472. else
  473. if tag(v) == TAG_NUMBER then
  474. flag_set(flags, f, v)
  475. elseif tag(v) == TAG_TABLE then
  476. flag_set_full(flags, f, FLAG_FLAG_BOTH, v[1], v[2])
  477. elseif tag(v) == TAG_FLAG then
  478. flag_set_full(flags, f, FLAG_FLAG_FLAGS, 0, 0, v)
  479. elseif tag(v) == TAG_FUNCTION then
  480. local ret = get_flag_value["function"](v, {__index__=0 __type__=-1000})
  481. flag_set_full(flags, f, FLAG_FLAG_BOTH, ret[1], ret[2])
  482. else
  483. local typ = TAG_TO_FLAG_TYPE[tag(v)]
  484. if typ[2] then v = __wrap_ptr[typ[2]](v) end
  485. flag_set_ptr(flags, f, typ[1], v)
  486. end
  487. end
  488. else
  489. old_flags_type_tags.settable(flags, f, v)
  490. end
  491. end
  492. )
  493. old_flags_type_tags.index = settagmethod(tag(player.flags), "index",
  494. function(flags, f, v)
  495. if tag(f) == TAG_NUMBER then
  496. if v == nil then
  497. flag_remove(flags, f)
  498. else
  499. if tag(v) == TAG_NUMBER then
  500. flag_set(flags, f, v)
  501. elseif tag(v) == TAG_FLAG then
  502. flag_set_full(flags, f, FLAG_FLAG_FLAGS, 0, 0, v)
  503. else
  504. local typ = TAG_TO_FLAG_TYPE[tag(v)]
  505. if typ[2] then v = __wrap_ptr[typ[2]](v) end
  506. flag_set_ptr(flags, f, typ[1], v)
  507. end
  508. end
  509. else
  510. old_flags_type_tags.index(flags, f, v)
  511. end
  512. end
  513. )
  514. old_flags_type_tags.fct = settagmethod(tag(player.flags), "function",
  515. function(...)
  516. if type(arg[2]) == "table" then
  517. grab_flags(arg[2], arg[1], nil)
  518. elseif tag(arg[2]) == TAG_NUMBER then
  519. return arg[1][arg[2]] or 0
  520. else
  521. return call(old_flags_type_tags.fct, arg)
  522. end
  523. end
  524. )
  525. -- Make module(not game modules, code modules, we'll call them namespaces) also act as functions
  526. constant("TAG_NAMESPACE", tag(fs))
  527. settagmethod(TAG_NAMESPACE, "function",
  528. function(module, ...)
  529. assert(module.__exec_module, "This namespace is not callable!")
  530. return call(module.__exec_module, arg)
  531. end
  532. )
  533. -- Assign the various namespaces self function
  534. rng.__exec_module = function(a, b)
  535. if b then
  536. return rng.range(a, b)
  537. else
  538. return rng.range(1, a)
  539. end
  540. end
  541. rng.arange = function(a, b)
  542. if a < b then
  543. return rng.range(a, b)
  544. else
  545. return rng.range(b, a)
  546. end
  547. end
  548. -- Switch the simple rng on/off
  549. rng.simple = function(seed)
  550. -- Switch on
  551. if seed then
  552. rng.Rand_quick = true
  553. rng.Rand_value = seed
  554. -- Switch off
  555. else
  556. rng.Rand_quick = nil
  557. end
  558. end
  559. -- Is the simple rng on ?
  560. rng.is_simple = function(seed)
  561. return rng.Rand_quick
  562. end
  563. -- Describe a roll
  564. rng.desc_roll = function(x, y)
  565. return x.."to"..(x*y)
  566. end
  567. -- Describe a roll
  568. rng.desc_range = function(x, y)
  569. return x.."to"..y
  570. end
  571. message.__exec_module = function(color, msg)
  572. if msg then message.cmsg_print(color, msg)
  573. else message.msg_print(color)
  574. end
  575. end
  576. term.print = function(color, msg, y, x)
  577. if x then
  578. term.c_put_str(color, msg, y, x)
  579. else
  580. term.put_str(color, msg, y)
  581. end
  582. end
  583. term.blank_print = function(color, msg, y, x)
  584. if x then
  585. term.c_prt(color, msg, y, x)
  586. else
  587. term.prt(color, msg, y)
  588. end
  589. end
  590. term.text_out = function(c, msg)
  591. if msg then
  592. term.text_out_c(c, msg)
  593. else
  594. term.text_out_c(color.WHITE, c)
  595. end
  596. end
  597. term.display = function(c, msg, y ,x)
  598. if not x then
  599. c, msg, y, x = color.WHITE, c, msg, y
  600. end
  601. message.display(x, y, strlen(msg), c, msg)
  602. end
  603. function term.drop_text_left(c, str, y, o)
  604. local i = strlen(str)
  605. local x = 39 - (strlen(str) / 2) + o
  606. while (i > 0)
  607. do
  608. local a = 0
  609. local time = 0
  610. if (strbyte(str, i) ~= strbyte(" ", 1)) then
  611. while (a < x + i - 1)
  612. do
  613. term.putch(a - 1, y, c, 32)
  614. term.putch(a, y, c, strbyte(str, i))
  615. time = time + 1
  616. if time >= 4 then
  617. term.xtra(TERM_XTRA_DELAY, 1)
  618. time = 0
  619. end
  620. term.redraw_section(a - 1, y, a, y)
  621. a = a + 1
  622. term.inkey_scan = true
  623. if (term.inkey() ~= 0) then
  624. return true
  625. end
  626. end
  627. end
  628. i = i - 1
  629. end
  630. return false
  631. end
  632. function term.drop_text_right(c, str, y, o)
  633. local x = 39 - (strlen(str) / 2) + o
  634. local i = 1
  635. while (i <= strlen(str))
  636. do
  637. local a = 79
  638. local time = 0
  639. if (strbyte(str, i) ~= strbyte(" ", 1)) then
  640. while (a >= x + i - 1)
  641. do
  642. term.putch(a + 1, y, c, 32)
  643. term.putch(a, y, c, strbyte(str, i))
  644. time = time + 1
  645. if time >= 4 then
  646. term.xtra(TERM_XTRA_DELAY, 1)
  647. time = 0
  648. end
  649. term.redraw_section(a, y, a + 1, y)
  650. a = a - 1
  651. term.inkey_scan = true
  652. if (term.inkey() ~= 0) then
  653. return true
  654. end
  655. end
  656. end
  657. i = i + 1
  658. end
  659. return false
  660. end
  661. function term.get(y, x)
  662. local ret, a, c = term.Term_what(x, y)
  663. if ret ~= 0 then return nil end
  664. return a, c
  665. end
  666. -- Strings
  667. function strcap(str)
  668. if strlen(str) > 1 then
  669. return strupper(strsub(str, 1, 1))..strsub(str, 2)
  670. elseif strlen(str) == 1 then
  671. return strupper(str)
  672. else
  673. return str
  674. end
  675. end
  676. function msg_format(...)
  677. message(call(format, arg))
  678. end
  679. -- Stacks
  680. function stack_push(stack, val)
  681. tinsert(stack, val)
  682. end
  683. function stack_pop(stack)
  684. if getn(stack) >= 1 then
  685. return tremove(stack)
  686. else
  687. error("Tried to unstack an empty stack")
  688. return nil
  689. end
  690. end
  691. -- Bounds a value
  692. function bound(val, min, max)
  693. if val < min then return min
  694. elseif max and val > max then return max
  695. else return val
  696. end
  697. end
  698. -- choose_from_list
  699. --
  700. -- Reasonably generic chooser in LUA for ToME
  701. --
  702. -- It tries as far as possible to imitate the look of the built in
  703. -- spell choosers, and should be usable for a wide variety of special
  704. -- powers that module authors might devise
  705. --
  706. -- parameters:
  707. --
  708. -- array: The array of 'things' to choose from. They can be whatever
  709. -- you like: numbers, strings, tables, etc, as long as they are
  710. -- in an array
  711. -- what: What to call them for the prompt. E.g. "Spells"
  712. -- prompt:The last bit of the prompt, e.g. "Cast which spell?"
  713. -- shortprinter:
  714. -- Shortprinter is the function called to print each line of the
  715. -- list. It is called with three parameters : y,x,thing. It should
  716. -- use prt or c_prt to print information at y,x. The thing is
  717. -- some element of the array you gave to choose_from_list.
  718. -- Shortprinter should also expect to be called with 'thing' set to
  719. -- nil, in which case it should print a 'column heading' line.
  720. -- longprinter:
  721. -- Longprinter is the function called to print the long description,
  722. -- requested by the user when they press a capital letter key. It is
  723. -- called with two parameters, a y position and the thing to describe.
  724. -- Consider using text_out or text_out_c because they wraps
  725. -- lines for you.
  726. --
  727. -- Bugs:
  728. --
  729. -- Pressing a capital letter to request a long description will try to print
  730. -- a list anyway. Make sure that shortprinter is defined if longprinter is.
  731. --
  732. -- ToDo:
  733. -- '@' style extended selection
  734. function choose_from_list(array,what,prompt,shortprinter,longprinter)
  735. local max = getn(array)
  736. local redraw = game_options.easy_inven
  737. local ret = {nil,nil}
  738. local dolist,promptlist = false,""
  739. local dodesc,promptdesc = false,""
  740. -- We can only display a list if the caller provided a
  741. -- shortprinter function
  742. if shortprinter then
  743. dolist = true
  744. promptlist = ", *=List"
  745. end
  746. -- We can only display long descriptions if the caller provided a
  747. -- longprinter function
  748. if longprinter then
  749. dodesc = true
  750. promptdesc = format(", Descs A-%c",strbyte("A")-1+max)
  751. end
  752. term.blank_print(format("(%s a-%c%s%s, ESC=exit) %s",what,strbyte("a")-1+max,promptdesc,promptlist,prompt), 0, 0)
  753. term.save()
  754. if redraw then choose_print_list(array,shortprinter) end
  755. while not nil do
  756. local c = term.inkey()
  757. if (c == ESCAPE) then
  758. ret = {nil,nil}
  759. break
  760. end
  761. if c == strbyte('@') then
  762. -- '@' style extended selection
  763. if getn(array) == 0 then
  764. ret = {nil,nil}
  765. break
  766. end
  767. local typ = type(array[1])
  768. if not (typ == "string" or typ == "table") then
  769. -- Only do strings and tables as of now
  770. ret = {nil,nil}
  771. break
  772. end
  773. local str = get_string(prompt .. " [by name] ") or ""
  774. str = clean_whitespace(str)
  775. if typ == "string" and str == "" then
  776. ret = {nil,nil}
  777. break
  778. end
  779. ret = nil
  780. for i = 1, getn(array) do
  781. if typ == "string" then
  782. if strlower(str) == strlower(array[i]) then
  783. ret = {array[i], i}
  784. break
  785. end
  786. else
  787. if array[i][str] or (array[i].name and
  788. strlower(array[i].name) ==
  789. strlower(str))
  790. then
  791. ret = {array[i], i}
  792. break
  793. end
  794. end
  795. end -- for i = 1, getn(array) do
  796. if ret then
  797. break
  798. end
  799. -- No match found
  800. ret = {nil,nil,str}
  801. break
  802. end -- '@' style extended selection
  803. if (dolist == true and (c == strbyte('*') or c == strbyte(' ') or c == strbyte('?'))) then
  804. if (redraw == true) then -- list already displayed, remove it
  805. term.load()
  806. term.save()
  807. redraw = false
  808. else
  809. choose_print_list(array,shortprinter)
  810. redraw = true
  811. end
  812. end
  813. if (c - strbyte('a') >= 0) and (c - strbyte('a') < max) then
  814. ret = { array[(c-strbyte('a')+1)], c-strbyte('a')+1 }
  815. break
  816. end
  817. if (dodesc == true and (c - strbyte('A') >= 0) and (c - strbyte('A') < max)) then
  818. if (redraw == true) then
  819. term.load()
  820. term.save()
  821. end
  822. local index,line,y
  823. y = choose_print_list(array,shortprinter)
  824. term.blank_print("",y,0)
  825. longprinter(y,array[1+c-strbyte('A')])
  826. redraw = true
  827. end
  828. end
  829. term.load()
  830. term.blank_print("",0,0)
  831. return unpack(ret)
  832. end
  833. function choose_print_list(array,shortprinter)
  834. local col
  835. local y=1
  836. term.blank_print("",y,0)
  837. shortprinter(y,3,nil) -- heading
  838. y = y + 1
  839. for i = 1,getn(array) do
  840. term.blank_print(color.LIGHT_BLUE,format("%c) ",strbyte("a")+i-1),y,0)
  841. shortprinter(y,3,array[i])
  842. y = y + 1
  843. end
  844. return y
  845. end
  846. function book_capitals(s)
  847. local words = strsplit(s, " ")
  848. for i = 1, getn(words) do
  849. local word = words[i]
  850. -- Don't capitalize certain words unless they are at the begining
  851. -- of the string.
  852. if i == 1 or (word ~= "of" and word ~= "the" and word ~= "and" and
  853. word ~= "a" and word ~= "an")
  854. then
  855. words[i] = gsub(word, "^(.)",
  856. function(x)
  857. return strupper(x)
  858. end)
  859. end
  860. end
  861. return strjoin(words, " ")
  862. end
  863. -- Apply a function to each flags
  864. function foreach_flags(flags, fct)
  865. for i = 1, flags.size do
  866. if (flags.node[i].flags & FLAG_FLAG_USED) ~= 0 then
  867. fct(flags, flags.node[i].key)
  868. end
  869. end
  870. end
  871. -- Fold together an array using a start value and a binary function
  872. -- f. (E.g., take a sum)
  873. function fold(array,start,f)
  874. local acc = start
  875. for i = 1,getn(array) do
  876. acc = f(acc,array[i])
  877. end
  878. return acc
  879. end
  880. -- Print only in wiz mode
  881. function wiz_print(s)
  882. if (wizard == true) then message(s) end
  883. end
  884. -- Convert a desc array to a desc string with \n
  885. function convert_desc(tdesc, sep)
  886. if not sep then sep = " " end
  887. if type(tdesc) == "string" then return tdesc end
  888. local desc = ""
  889. for i = 1, getn(tdesc) do
  890. desc = desc..tdesc[i]
  891. if i < getn(tdesc) then desc = desc..sep end
  892. end
  893. return desc
  894. end
  895. function max(a, b)
  896. if a > b then return a else return b end
  897. end
  898. function min(a, b)
  899. if a < b then return a else return b end
  900. end
  901. function abs(v)
  902. if v < 0 then return -v else return v end
  903. end
  904. function default_value(table, index, v)
  905. if not table[index] then table[index] = v end
  906. end
  907. -- Physfs
  908. fs.add_to_search_path_real = fs.add_to_search_path
  909. fs.add_to_search_path = function(file, append, mount)
  910. if mount then fs.PHYSFS_specific_mount = mount end
  911. local ret = fs.add_to_search_path_real(file, append)
  912. fs.PHYSFS_specific_mount = nil
  913. return ret
  914. end
  915. -- Add keypresses
  916. global("__new_key_action_next", -8000)
  917. function new_key_action(t)
  918. assert(t.action, "no key action")
  919. if type(t.key) == "string" then t.key = strbyte(t.key) end
  920. if t.extended then
  921. assert(t.desc, "No extended key description")
  922. if not t.key then
  923. t.key = __new_key_action_next
  924. __new_key_action_next = __new_key_action_next + 1
  925. end
  926. cli_add(tostring(t.key), t.extended, t.desc)
  927. end
  928. -- Add it
  929. hook
  930. {
  931. [hook.KEYPRESS] = function (key)
  932. if key == %t.key then
  933. return %t.action()
  934. end
  935. end,
  936. }
  937. end
  938. -- Merge 2 tables
  939. function merge_tables(t1, t2)
  940. for i = 1, getn(t2) do
  941. tinsert(t1, t2[i])
  942. end
  943. return t1
  944. end
  945. function pack(...)
  946. return arg
  947. end
  948. function unpack(table, idx)
  949. if getn(table) == 0 then return
  950. elseif getn(table) == 1 then return table[1]
  951. else
  952. if not idx then
  953. return table[1], unpack(table, 2)
  954. else
  955. if idx < getn(table) then
  956. return table[idx], unpack(table, idx + 1)
  957. else
  958. return table[idx]
  959. end
  960. end
  961. end
  962. end
  963. -- Display text with embedded color info
  964. function term.color_text(color, t, y, x)
  965. message.display(x, y, strlen(t), color, t)
  966. end
  967. -- Compute average vlue
  968. function average_value(...)
  969. local avg, nb = 0, 0
  970. if nb == getn(arg) then error("Average of 0 values!") end
  971. for i = 1, getn(arg) do
  972. if type(arg[i]) == "table" then
  973. avg = avg + (arg[i][1] * arg[i][2])
  974. nb = nb + arg[i][2]
  975. else
  976. avg = avg + arg[i]
  977. nb = nb + 1
  978. end
  979. end
  980. return avg / nb
  981. end
  982. -- A way to check if the game is now running(as opposed to initialization/character gen)
  983. global("game", {})
  984. settag(game, TAG_NAMESPACE) -- Tag game as a namespace
  985. hook(hook.GAME_START, function() game.started = true end)
  986. -- A flagset iterator function
  987. constant("flag_iterate", function(flags, f, ...)
  988. for i = 1, flags.size do
  989. if (flags.node[i].flags & FLAG_FLAG_USED) ~= 0 then
  990. f(flags, flags.node[i].key, unpack(arg))
  991. end
  992. end
  993. end)
  994. function iif(a,b,c)
  995. if a then return b else return c end
  996. end
  997. -- Copy a table recursively(copies only lua native elements, wont work for userdata)
  998. function table_copy(t1, t2)
  999. local dest, src = nil, nil
  1000. if not t2 then
  1001. -- Return a copy of t1
  1002. dest = {}
  1003. src = t1
  1004. else
  1005. -- Copy everything from t2 into t1
  1006. dest = t1
  1007. src = t2
  1008. end
  1009. -- Handle metatag
  1010. if tag(dest) == TAG_TABLE and tag(src) ~= TAG_TABLE then
  1011. settag(dest, tag(src))
  1012. elseif tag(dest) == tag(src) then
  1013. else
  1014. error("table_copy cannot handle different tags")
  1015. end
  1016. for k, e in src do
  1017. if tag(e) == TAG_FLAG then
  1018. if not dest[k] then
  1019. dest[k] = flag_new()
  1020. end
  1021. flag_copy(dest[k], e)
  1022. elseif type(e) == "table" then
  1023. if dest[k] then
  1024. table_copy(dest[k], e)
  1025. else
  1026. dest[k] = table_copy(e)
  1027. end
  1028. else
  1029. dest[k] = e
  1030. end
  1031. end
  1032. return dest
  1033. end
  1034. -- Join an array of strings into one, separated by the given
  1035. -- character(s)
  1036. function strjoin(strs, char, last)
  1037. local out = ""
  1038. for i = 1, getn(strs) do
  1039. out = out .. tostring(strs[i])
  1040. if last and i == (getn(strs) - 1) then
  1041. out = out .. last
  1042. elseif i < getn(strs) then
  1043. out = out .. char
  1044. end
  1045. end
  1046. return out
  1047. end
  1048. -- Split a string by the given character(s)
  1049. function strsplit(str, char)
  1050. local ret = {}
  1051. local len = strlen(str)
  1052. local start = 1
  1053. while true do
  1054. local split_start, split_end = strfind(str, char, start)
  1055. if not split_start then
  1056. tinsert(ret, strsub(str, start))
  1057. break
  1058. end
  1059. tinsert(ret, strsub(str, start, split_start - 1))
  1060. if split_end == len then
  1061. break
  1062. end
  1063. start = split_end + 1
  1064. end
  1065. return ret
  1066. end
  1067. -- Is the spot very much plain ?
  1068. function cave_plain_floor(c, y)
  1069. if y then c = cave(c , y) end
  1070. return has_flag(f_info[1 + c.feat], FLAG_FLOOR) and not has_flag(f_info[1 + c.feat], FLAG_REMEMBER)
  1071. end
  1072. -- Converts a number into a string with the sign
  1073. -- I.e: 3 => +3 ; -3 => -3
  1074. function signed_number_tostring(v)
  1075. if v >= 0 then return "+"..v
  1076. else return tostring(v) end
  1077. end
  1078. -- Create a new flag only if it doesn't exist
  1079. function safe_new_flag(s)
  1080. if not globals()["FLAG_"..s] then new_flag(s) end
  1081. end
  1082. -- Strip multiple spaces, leading whitespace and trailing whitespace.
  1083. function clean_whitespace(str)
  1084. if strlen(str) == 0 then
  1085. return str
  1086. end
  1087. str = gsub(str, "\t", " ")
  1088. -- Split string on space, then put the string back together,
  1089. -- ignoring empty elements (which were multiple spaces in a row)
  1090. local parts = strsplit(str, " ")
  1091. local outstr = ""
  1092. for i = 1, getn(parts) do
  1093. if strlen(parts[i]) > 0 then
  1094. if strlen(outstr) > 0 then
  1095. outstr = outstr .. " "
  1096. end
  1097. outstr = outstr .. parts[i]
  1098. end
  1099. end
  1100. return outstr
  1101. end
  1102. -- Tests if str ends with 'ending'
  1103. function ends_with(str, ending)
  1104. local len = strlen(str)
  1105. local end_len = strlen(ending)
  1106. if len < end_len then
  1107. return nil
  1108. end
  1109. if strfind(str, ending, len - end_len + 1, true) then
  1110. return true
  1111. end
  1112. return nil
  1113. end
  1114. -- Tests if str starts with 'start'
  1115. function starts_with(str, start)
  1116. local len = strlen(str)
  1117. local start_len = strlen(start)
  1118. if len < start_len then
  1119. return nil
  1120. end
  1121. if strfind(str, start, 1, true) == 1 then
  1122. return true
  1123. end
  1124. return nil
  1125. end
  1126. -- Gives prompt to user and gets back a string
  1127. function get_string(prompt, default)
  1128. if not prompt then
  1129. prompt = ""
  1130. end
  1131. -- Clear message area
  1132. message.msg_print()
  1133. -- Display prompt
  1134. term.prt(prompt, 0, 0)
  1135. -- Get result
  1136. local str = term.askfor_aux(default)
  1137. -- Clear prompt
  1138. term.prt("", 0, 0)
  1139. return str
  1140. end
  1141. --
  1142. -- If the object is not known/identified, then return the object kind
  1143. -- structure for the object, to tell the user generic info about
  1144. -- that type of object
  1145. --
  1146. constant("__generic_obj", new_object())
  1147. function generic_or_specific(obj)
  1148. local kind
  1149. if tag(obj) == TAG_OBJECT then
  1150. if is_known(obj) then
  1151. return obj
  1152. end
  1153. kind = k_info(obj.k_idx)
  1154. else
  1155. kind = obj
  1156. obj = {k_idx = lookup_kind(kind.tval, kind.sval)}
  1157. end
  1158. -- Set up a generic version of the object
  1159. __generic_obj.k_idx = obj.k_idx
  1160. __generic_obj.d_attr = obj.d_attr or kind.d_attr
  1161. __generic_obj.x_attr = obj.x_attr or kind.x_attr
  1162. __generic_obj.number = obj.number or 1
  1163. __generic_obj.marked = obj.marked or 0
  1164. __generic_obj.weight = obj.weight or kind.weight
  1165. __generic_obj.note = obj.note or 0
  1166. __generic_obj.tval = kind.tval
  1167. __generic_obj.sval = kind.sval
  1168. flag_empty(__generic_obj.flags)
  1169. flag_copy(__generic_obj.flags, kind.flags)
  1170. -- Recursively remove anything that gets its value randomly
  1171. -- assigned at object creation time, since those random values
  1172. -- are specific to each created object and not generic at all.
  1173. flag_remove_rand(__generic_obj.flags)
  1174. return __generic_obj
  1175. end
  1176. --
  1177. -- Returns the dungeon level or the feature, if the player is not
  1178. -- in a dungeon
  1179. --
  1180. function level_or_feat(dtype, dlevel)
  1181. if dlevel == 0 or player.wild_mode then
  1182. return wild_map(player.wilderness_y, player.wilderness_x).feat
  1183. else
  1184. return dlevel
  1185. end
  1186. end
  1187. --
  1188. -- Determine if a command should be prevented, based on the item's
  1189. -- inscription
  1190. --
  1191. function check_prevent_cmd(obj, cmd)
  1192. if not cmd then
  1193. cmd = command_cmd
  1194. end
  1195. if type(cmd) == "string" then
  1196. if strlen(cmd) ~= 1 then
  1197. error("'cmd' must be a single character")
  1198. end
  1199. cmd = strbyte(cmd)
  1200. end
  1201. return check_prevent_cmd_aux(obj, cmd)
  1202. end
  1203. --
  1204. -- Determine if a "legal" grid can be "seen" by the player
  1205. --
  1206. function player_can_see_bold(y, x)
  1207. return (cave(y, x).info & CAVE_SEEN ~= 0)
  1208. end
  1209. function who_to_faction(who)
  1210. if who == 0 then
  1211. return FACTION_PLAYER
  1212. elseif who > 0 then
  1213. local monst = monster(who)
  1214. assert(monst, "No monsters for 'who' " .. who)
  1215. return monst.faction
  1216. elseif who == WHO_DUNGEON then
  1217. return FACTION_DUNGEON
  1218. elseif who == WHO_TRAP then
  1219. error("No faction for WHO_TRAP")
  1220. elseif who == WHO_FLOOR then
  1221. error("No faction for WHO_FLOOR")
  1222. end
  1223. error("Unnown 'who' value " .. who)
  1224. end
  1225. ---
  1226. --- A shortened name for get_function_registry_from_flag()
  1227. ---
  1228. function get_func_from_flag(flags, flag_name)
  1229. return get_function_registry_from_flag(flags, flag_name)
  1230. end
  1231. ---
  1232. --- Try to find a string in the object's note, if it has a note
  1233. ---
  1234. function notefind(obj, str)
  1235. if obj.note == 0 then
  1236. return false
  1237. end
  1238. return strfind(quark_str(obj.note), str, 1, true)
  1239. end
  1240. --
  1241. -- Returns the CTRL key equivalent of a letter
  1242. --
  1243. function KTRL(char)
  1244. char = strlower(char)
  1245. local byte = strbyte(char)
  1246. if byte < strbyte('a') or byte > strbyte('z') then
  1247. return nil
  1248. end
  1249. return (byte - strbyte('a') + 1)
  1250. end
  1251. --
  1252. -- The following is something like get_function_registry_from_flag(),
  1253. -- but for functions that aren't in the registry. It saves the name
  1254. -- of the function, rather than its bytecode, so anonymous functions
  1255. -- can't be used.
  1256. --
  1257. __function_indexing = {}
  1258. __function_indexing.indexes = {}
  1259. __function_indexing.strings = {}
  1260. add_loadsave("__function_indexing.indexes", {})
  1261. add_loadsave("__function_indexing.strings", {})
  1262. function function_to_index(func)
  1263. assert(tag(func) == TAG_STRING, "Function must be specified via its name")
  1264. assert(func ~= "", "Function name is empty")
  1265. if __function_indexing.strings[func] then
  1266. return __function_indexing.strings[func]
  1267. end
  1268. local index = getn(__function_indexing.indexes) + 1
  1269. __function_indexing.indexes[index] = func
  1270. __function_indexing.strings[func] = index
  1271. return index
  1272. end
  1273. function index_to_function(index)
  1274. assert(index <= getn(__function_indexing.indexes),
  1275. "No such function is indexed: " .. index)
  1276. assert(index >= 1, "No such function is indexed: " .. index)
  1277. local name = __function_indexing.indexes[index]
  1278. return getglobal(name), name
  1279. end
  1280. -- Pick up an item without having to deal with internals
  1281. function object_pickup_simple(obj, item)
  1282. __core_objects.clean_get_item()
  1283. item = generate_item_floor_index(obj.iy, obj.ix, -item)
  1284. return object_pickup(item)
  1285. end
  1286. -- Division that rounds up/down to the nearest integer
  1287. function div_round(quotient, divisor)
  1288. if quotient == 0 then
  1289. return 0
  1290. end
  1291. local out = quotient / divisor
  1292. local remainer = imod(abs(quotient), divisor)
  1293. if (remainer * 2) >= divisor then
  1294. out = out + (quotient / abs(quotient))
  1295. end
  1296. return out
  1297. end
  1298. --- @fn
  1299. -- @brief Displays a color coded file. The screen should probably be saved firts with term.save()
  1300. function display_colored_file(file)
  1301. local fff = fs.open(file, "r")
  1302. if fff then
  1303. local line = nil
  1304. local y = 0
  1305. repeat
  1306. line = fs.read(fff)
  1307. if line then
  1308. term.display(line, y, 0)
  1309. y = y + 1
  1310. end
  1311. until line == nil
  1312. fs.close(fff)
  1313. return true
  1314. end
  1315. end
  1316. --- @fn
  1317. -- @brief Swicth implementation, use as: swicth(cond){ [val1] = function()...end [val2] = function()...end ... }
  1318. function switch(cond)
  1319. return function(t)
  1320. t[%cond]()
  1321. end
  1322. end
  1323. --- @fn
  1324. -- @brief Returns whether or not a skill is known
  1325. function skills.is_known(skill)
  1326. if wizard then
  1327. return true
  1328. end
  1329. if get_skill(skill) > 0 or s_info(skill).mod > 0 then
  1330. return true
  1331. end
  1332. for i = 0, max_s_idx - 1 do
  1333. if s_info(i).father == skill and skills.is_known(i) then
  1334. return true
  1335. end
  1336. end
  1337. return false
  1338. end
  1339. --- @fn
  1340. -- @brief Returns whether or not a skill has any children skills
  1341. function skills.has_child(skill)
  1342. for i = 0, max_s_idx - 1 do
  1343. if s_info(i).father == skill and skills.is_known(i) then
  1344. return true
  1345. end
  1346. end
  1347. return false
  1348. end
  1349. --- @fn
  1350. -- @brief Returns a tree data-structure representation of the list of
  1351. -- skills
  1352. function skills.get_tree(father)
  1353. local skill_nodes = {}
  1354. father = father or -1
  1355. for i = 0, max_s_idx - 1 do
  1356. if s_info(i).father == father and skills.is_known(i) and
  1357. not s_info(i).hidden
  1358. then
  1359. local info = {}
  1360. info.skill = i
  1361. info.children = skills.get_tree(i)
  1362. if getn(info.children) == 0 then
  1363. info.children = nil
  1364. end
  1365. tinsert(skill_nodes, info)
  1366. end
  1367. end -- for i = 1, max_s_idx do
  1368. return skill_nodes
  1369. end -- skills.get_tree()
  1370. --- @fn
  1371. -- @brief Produces all possible ordered combinations of a list of
  1372. -- elements
  1373. function permutate(list)
  1374. if getn(list) == 1 then
  1375. return {{list[1]}}
  1376. end
  1377. local out_list = {}
  1378. for i = 1, getn(list) do
  1379. local first = tremove(list, i)
  1380. local small_list = permutate(list)
  1381. for j = 1, getn(small_list) do
  1382. local arr = small_list[j]
  1383. tinsert(arr, 1, first)
  1384. tinsert(out_list, arr)
  1385. end
  1386. tinsert(list, i, first)
  1387. end
  1388. return out_list
  1389. end -- permutate()
  1390. --- @fn
  1391. -- @brief Determines if the given substring has a word boundry immediatly
  1392. -- before and after it in the target string.
  1393. function is_separate_word(word, str)
  1394. -- Test for 'word == string' before escaping pattern characters
  1395. -- since escaping might change length of word
  1396. if word == str then
  1397. return true
  1398. end
  1399. -- First, escape any characters that strfind() might interpret
  1400. -- as a pattern
  1401. local word_len = strlen(word)
  1402. word = gsub(word, "([%$%^%%%(%)%.%[%]%*%+%-%?])", "%%%1")
  1403. if not strfind(str, word) then
  1404. return false
  1405. end
  1406. -- Is word?
  1407. if strlen(word) == strlen(str) then
  1408. return true
  1409. end
  1410. -- Word is somewhere in the middle?
  1411. if strfind(str, "[^%w_-]" .. word .. "[^%w_-]") then
  1412. return true
  1413. end
  1414. -- Starts with word?
  1415. if strfind(str, word .. "[^%w_-]") == 1 then
  1416. return true
  1417. end
  1418. -- Ends with word?
  1419. local pos = strlen(str) - word_len
  1420. if strfind(str, "[^%w_-]" .. word, pos) == pos then
  1421. return true
  1422. end
  1423. return false
  1424. end -- is_separate_word()
  1425. function show_arr_as_file(arr, what, line, mode)
  1426. what = what or ""
  1427. line = line or 0
  1428. mode = mode or 0
  1429. if not make_temp_file() then
  1430. return false
  1431. end
  1432. local file = get_temp_name()
  1433. if not file or file == "" then
  1434. end_temp_file()
  1435. return false
  1436. end
  1437. for i = 1, getn(arr) do
  1438. write_temp_file(arr[i])
  1439. end
  1440. -- Close temp file to flush contents to disk
  1441. close_temp_file()
  1442. local ret = show_file(file, what, line, mode)
  1443. -- Delete temp file
  1444. end_temp_file()
  1445. return ret
  1446. end -- show_arr_as_file()
  1447. -- An auxilliary function which takes care of the moving up or down
  1448. -- in a displayed list. See show_arr_as_list() for an example of
  1449. -- how to use.
  1450. function display_list_simple(array, title, state)
  1451. assert(state and type(state) == "table",
  1452. "Needs a table to store state passed as third argument")
  1453. if not state.x then
  1454. local ret, wid, hgt = term.get_size()
  1455. state.x = 0
  1456. state.y = 0
  1457. state.hgt = hgt
  1458. state.wid = wid
  1459. state.color = state.color or color.LIGHT_BLUE
  1460. state.begin = state.begin or 1
  1461. state.sel = state.sel or 1
  1462. end
  1463. local per_page = state.per_page or state.hgt - 2
  1464. local size = getn(array)
  1465. display_list(state.y, state.x, state.hgt - 1, state.wid - 1, title,
  1466. array, state.begin, state.sel, state.color,
  1467. nil, true)
  1468. if state.footer then
  1469. term.c_put_str(state.color, state.footer,
  1470. state.y + state.hgt - 1,
  1471. (state.wid - strlen(state.footer)) / 2 + state.x)
  1472. end
  1473. local key = term.inkey()
  1474. local c = strchar(key)
  1475. if c == "8" then
  1476. -- up key
  1477. state.sel = state.sel - 1
  1478. elseif c == "2" then
  1479. -- down key
  1480. state.sel = state.sel + 1
  1481. elseif c == "9" then
  1482. -- page up key
  1483. state.sel = state.sel - per_page + 1
  1484. elseif c == "3" then
  1485. -- page down key
  1486. state.sel = state.sel + per_page
  1487. elseif c == "7" then
  1488. -- home key
  1489. state.sel = 1
  1490. elseif c == "1" then
  1491. -- End key
  1492. state.sel = size
  1493. elseif c == " " then
  1494. -- Space key pages down, but wraps around to start if it
  1495. -- goes past the end
  1496. if state.sel >= size - per_page + 1 then
  1497. state.sel = 1
  1498. state.begin = 1
  1499. else
  1500. state.sel = state.sel + per_page
  1501. end
  1502. end
  1503. if state.sel < 1 then
  1504. state.sel = 1
  1505. elseif state.sel > size then
  1506. state.sel = size
  1507. end
  1508. if state.sel > state.begin + per_page then
  1509. state.begin = state.sel - per_page + 1
  1510. end
  1511. if state.sel < state.begin then
  1512. state.begin = state.sel
  1513. end
  1514. if state.begin > size then
  1515. state.begin = state.sel
  1516. end
  1517. if state.begin < 1 then
  1518. state.begin = 1
  1519. end
  1520. return state.sel, key
  1521. end -- display_list_simple()
  1522. function show_arr_as_list(to_show, title, dont_save)
  1523. local ret, wid, hgt = term.get_size()
  1524. local begin = 1
  1525. local per_page = hgt - 2
  1526. local std_footer = "ESC to quit"
  1527. local arr, func, state, info, footer
  1528. local to_func_chars, to_func_keys = {}, {}
  1529. local __state = {
  1530. color = color.WHITE,
  1531. begin = 1,
  1532. sel = 1,
  1533. }
  1534. if type(to_show) == "table" then
  1535. arr = to_show
  1536. footer = std_footer
  1537. elseif tag(to_show) == TAG_FUNCTION then
  1538. func = to_show
  1539. arr, info, state = func()
  1540. if info.title then
  1541. title = info.title
  1542. end
  1543. if info.footer then
  1544. footer = std_footer .. ", " .. info.footer
  1545. else
  1546. footer = std_footer
  1547. end
  1548. if info.to_func_chars then
  1549. to_func_chars = info.to_func_chars
  1550. end
  1551. if info.to_func_keys then
  1552. to_func_keys = info.to_func_keys
  1553. end
  1554. if info.begin then
  1555. __state.begin = info.begin
  1556. __state.sel = info.begin
  1557. end
  1558. else
  1559. error("Can't display type '" .. type(to_show) .. "'")
  1560. end
  1561. if not dont_save then
  1562. term.save()
  1563. end
  1564. while true do
  1565. local size = getn(arr)
  1566. local no_move = false
  1567. if size <= per_page then
  1568. no_move = true
  1569. end
  1570. if not footer then
  1571. footer = std_footer
  1572. end
  1573. local header
  1574. if no_move then
  1575. header = title
  1576. else
  1577. header = title .. " (showing " .. __state.begin .. "-" ..
  1578. min((__state.begin + per_page - 1), size) ..
  1579. " of " .. size .. " lines)"
  1580. end
  1581. term.clear()
  1582. __state.footer = footer
  1583. local sel, key = display_list_simple(arr, header, __state)
  1584. message.display((wid - strlen(footer)) / 2, hgt - 1,
  1585. strlen(footer) + 2, color.LIGHT_BLUE,
  1586. "[" .. footer .. "]")
  1587. local c = strchar(key)
  1588. if key == ESCAPE then
  1589. break
  1590. elseif to_func_chars[c] or to_func_keys[key] then
  1591. arr, info, state = func(state, c, key, begin, arr)
  1592. if info.title then
  1593. title = info.title
  1594. end
  1595. if info.footer then
  1596. footer = std_footer .. ", " .. info.footer
  1597. else
  1598. footer = std_footer
  1599. end
  1600. if info.to_func_chars then
  1601. to_func_chars = info.to_func_chars
  1602. end
  1603. if info.to_func_keys then
  1604. to_func_keys = info.to_func_keys
  1605. end
  1606. if info.begin then
  1607. __state.begin = info.begin
  1608. __state.sel = info.begin
  1609. end
  1610. end
  1611. if no_move then
  1612. __state.sel = 1
  1613. end
  1614. -- We're just diplaying the list, not selecting an item, so
  1615. -- make the start of the list equal to the selected item. That
  1616. -- way, the arrow key will cause the list to scroll down
  1617. -- immediatly.
  1618. __state.begin = __state.sel
  1619. end -- while true do
  1620. if not dont_save then
  1621. term.load()
  1622. end
  1623. end -- show_arr_as_list()
  1624. function singularize(str)
  1625. local letter = strlower(strsub(str, 1, 1))
  1626. local vowels = {
  1627. a = true,
  1628. e = true,
  1629. i = true,
  1630. o = true
  1631. u = true
  1632. }
  1633. if vowels[letter] then
  1634. return "an " .. str
  1635. else
  1636. return "a " .. str
  1637. end
  1638. end
  1639. ----------
  1640. -- Plurals
  1641. ----------
  1642. __special_plurals = {
  1643. }
  1644. __regexp_plurals = {
  1645. -- Other than "human", words ending in "man" that *don't* pluralize
  1646. -- to men are pretty rare. If a module uses such a word, it
  1647. -- can add the pluralization to the list of special plurals.
  1648. -- This case also takes care of "foowoman" to "foowomen"
  1649. -- pluralizations as well.
  1650. ["(%S*man)$"] =
  1651. function(x)
  1652. if strlower(x) == "human" then
  1653. return x .. "s"
  1654. else
  1655. return strsub(x, 1, strlen(x) - 3) .. "men"
  1656. end
  1657. end
  1658. }
  1659. function pluralize(str)
  1660. -- If it's "Foo of Bar", then pluraize Foo, not Bar
  1661. local of_pos = strfind(str, " [Oo]f ")
  1662. if of_pos then
  1663. return pluralize(strsub(str, 1, of_pos - 1)) ..
  1664. strsub(str, of_pos, -1)
  1665. end
  1666. if __special_plurals[strlower(str)] then
  1667. -- Lower-case it, then restore original capitalization
  1668. local new_str = __special_plurals[strlower(str)]
  1669. local first_char = strsub(str, 1, 1)
  1670. if first_char == strupper(first_char) then
  1671. new_str = gsub(new_str, "^(.)",
  1672. function(x)
  1673. return strupper(x)
  1674. end)
  1675. end
  1676. return new_str
  1677. end
  1678. for regexp, replace in __regexp_plurals do
  1679. if strfind(str, regexp) then
  1680. str = gsub(str, regexp, replace)
  1681. return str
  1682. end
  1683. end
  1684. -- Okay, lets do it normally
  1685. local len = strlen(str)
  1686. local one_char = strsub(str, len, len)
  1687. local two_chars = ""
  1688. if len > 1 then
  1689. two_chars = strsub(str, len - 1, len)
  1690. end
  1691. local plural, eat_len
  1692. if one_char == 's' or one_char == 'h' or one_char == 'x'then
  1693. -- "Cutlass-es" and "Torch-es" and "Box-es"
  1694. plural = "es"
  1695. eat_len = 0
  1696. elseif one_char == 'y' then
  1697. -- "Pony" -> "Ponies"
  1698. plural = "ies"
  1699. eat_len = 1
  1700. elseif two_chars == "ff" or two_chars == "fe" then
  1701. -- "Staff" -> "Staves", "Knife" -> "Knives"
  1702. plural = "ves"
  1703. eat_len = 2
  1704. elseif one_char == 'f' then
  1705. -- "Shelf" -> "Shelves"
  1706. plural = "ves"
  1707. eat_len = 1
  1708. else
  1709. plural = "s"
  1710. eat_len = 0
  1711. end
  1712. str = gsub(str, strrep('.', eat_len) .. "$", plural)
  1713. return str
  1714. end -- pluralize()
  1715. function add_special_plural(single, plural)
  1716. if type(single) == "table" then
  1717. for i, v in single do
  1718. __special_plurals[i] = v
  1719. end
  1720. else
  1721. __special_plurals[single] = plural
  1722. end
  1723. end
  1724. function add_regexp_plural(regexp, plural)
  1725. if type(regexp) == "table" then
  1726. for i, v in regexp do
  1727. __regexp_plurals[i] = v
  1728. end
  1729. else
  1730. __regexp_plurals[regexp] = plural
  1731. end
  1732. end
  1733. function line_breaks(str, max_len)
  1734. if strlen(str) < max_len then
  1735. return {str}
  1736. end
  1737. -- Split into words.
  1738. local parts = strsplit(str, " ")
  1739. -- Removing leading blanks
  1740. while true do
  1741. if not parts[1] or parts[1] == "" or parts[1] == " " then
  1742. tremove(parts, 1)
  1743. else
  1744. break
  1745. end
  1746. end
  1747. local lines = {}
  1748. str = ""
  1749. while getn(parts) > 0 do
  1750. local len = strlen(str)
  1751. len = len + strlen(parts[1]) + 1
  1752. if len >= max_len then
  1753. tinsert(lines, clean_whitespace(str))
  1754. str = tremove(parts, 1)
  1755. else
  1756. str = str .. " " .. tremove(parts, 1)
  1757. end
  1758. end -- if len >= 80 then
  1759. if str ~= "" then
  1760. tinsert(lines, clean_whitespace(str))
  1761. end
  1762. if lines[1] == "" then
  1763. tremove(lines, 1)
  1764. end
  1765. return lines
  1766. end -- line_breaks()
  1767. -- Apply a function to each monster on level
  1768. function foreach_monster(fct)
  1769. for i = 1, monst_list.size do
  1770. if (monst_list.node[i].flags & FLAG_FLAG_USED) ~= 0 then
  1771. local m_idx = monst_list.node[i].key
  1772. fct(monster(m_idx), m_idx)
  1773. end
  1774. end
  1775. end -- foreach_monster()
  1776. -- Let user choose from a list of strings
  1777. function choose_from_list_simple(array, title, dont_save, start_sel, footer)
  1778. if not dont_save then
  1779. term.save()
  1780. end
  1781. footer = footer or "[ESC to quit]"
  1782. local ret, wid, hgt = term.get_size()
  1783. local lines = hgt - 2
  1784. local begin = start_sel
  1785. if start_sel and getn(array) <= lines then
  1786. begin = 1
  1787. end
  1788. local disp_state = {sel = start_sel, begin = begin, footer = footer}
  1789. local sel, key = nil, nil
  1790. while true do
  1791. term.clear()
  1792. sel, key = display_list_simple(array, title,
  1793. disp_state)
  1794. if key == ESCAPE then
  1795. sel, key = nil, nil
  1796. break
  1797. elseif key == strbyte("\n") or key == strbyte("\r") then
  1798. break
  1799. end
  1800. end
  1801. if not dont_save then
  1802. term.load()
  1803. end
  1804. return sel
  1805. end -- choose_from_list_simple()
  1806. -- Wrapper around Lua strchar(), since inkey() can return negative
  1807. -- values.
  1808. function strchar(key)
  1809. if key < 0 then
  1810. return "\0"
  1811. else
  1812. return %strchar(key)
  1813. end
  1814. end
  1815. -- Accepts a default message and a list of variables. If any of the
  1816. -- variables is true, it returns true, and returns false otherwise.
  1817. -- Also, it returns true, it will try to give a message to the user:
  1818. -- if any of the true variables is a string, it will use the first
  1819. -- one which is a string as the message, otherwise it will use the
  1820. -- default message.
  1821. function or_msg(msg, ...)
  1822. local ret = false
  1823. for i = 1, getn(arg) do
  1824. if arg[i] then
  1825. ret = true
  1826. if tag(arg[i]) == TAG_STRING then
  1827. msg = arg[i]
  1828. break
  1829. end
  1830. end
  1831. end
  1832. if ret and msg then
  1833. message(msg)
  1834. end
  1835. return ret
  1836. end
  1837. function randomize_list(list)
  1838. local size = getn(list)
  1839. for i = 1, size do
  1840. local j = rng(1, size)
  1841. list[i], list[j] = list[j], list[i]
  1842. end
  1843. end
  1844. function choose_multi_column(prompt, strs, dont_save, min_cols, colors)
  1845. local chars = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
  1846. 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
  1847. 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  1848. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1',
  1849. '2', '3', '4', '5', '6', '7', '8', '9', '0', '[', ']', '{', '}', ';',
  1850. ':', '\'', '\"', ',', '.', '<', '>', '\\', '|', '`', '~'}
  1851. local char_to_num = {}
  1852. for i = 1, getn(chars) do
  1853. char_to_num[chars[i]] = i
  1854. end
  1855. if getn(strs) > getn(chars) then
  1856. message(color.LIGHT_RED, "WARNING: choose_multi_column() can " ..
  1857. "display at most " .. getn(chars) .. " choices; some" ..
  1858. "choices will be missing.")
  1859. end
  1860. local ret, wid, hgt = term.get_size()
  1861. local num = min(getn(strs), getn(chars))
  1862. local rows = hgt - 2
  1863. local cols = (num / rows) + iif(imod(num, rows) > 0, 1, 0)
  1864. cols = max(cols, (min_cols or 0))
  1865. local max_wid = (wid - (4 * cols - 1)) / cols
  1866. if not dont_save then
  1867. term.save()
  1868. end
  1869. colors = colors or {}
  1870. local choice
  1871. while not choice do
  1872. term.clear()
  1873. local cur_row, cur_col = 1, 1
  1874. for i = 1, num do
  1875. local str = chars[i] .. ") " .. strsub(strs[i], 1, max_wid)
  1876. local clr = colors[i] or color.WHITE
  1877. term.c_put_str(clr, str,
  1878. cur_row + 1, (cur_col - 1) * (max_wid + 4))
  1879. cur_row = cur_row + 1
  1880. if cur_row > rows then
  1881. cur_row = 1
  1882. cur_col = cur_col + 1
  1883. end
  1884. end -- for i = 1, num do
  1885. term.prt(prompt, 0, 0)
  1886. local key = term.inkey()
  1887. local char = strchar(key)
  1888. if key == ESCAPE then
  1889. break
  1890. end
  1891. choice = char_to_num[char]
  1892. if choice and choice > num then
  1893. choice = nil
  1894. end
  1895. end -- while true do
  1896. if not dont_save then
  1897. term.load()
  1898. end
  1899. return choice
  1900. end -- choose_multi_column()
  1901. function monster_strsub(str, monst, flags)
  1902. flags = flags or 0
  1903. local name = monster_desc(monst, flags)
  1904. local pronoun = monster_desc(monst, flags | 32)
  1905. local possessive = monster_desc(monst, flags | 32 | 2)
  1906. local reflexive = monster_desc(monst, flags | 32 | 2 | 1)
  1907. str = gsub(str, ("@name@"), name)
  1908. str = gsub(str, ("@Name@"), strcap(name))
  1909. str = gsub(str, ("@pronoun@"), pronoun)

Large files files are truncated, but you can click here to view the full file