summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/lua
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/lua')
-rw-r--r--crawl-ref/source/lua/base.lua74
-rw-r--r--crawl-ref/source/lua/chnkdata.lua35
-rw-r--r--crawl-ref/source/lua/eat.lua97
-rw-r--r--crawl-ref/source/lua/gearset.lua206
-rw-r--r--crawl-ref/source/lua/gourmand.lua145
-rw-r--r--crawl-ref/source/lua/kills.lua240
-rw-r--r--crawl-ref/source/lua/runrest.lua108
-rw-r--r--crawl-ref/source/lua/safechunk.lua41
-rw-r--r--crawl-ref/source/lua/stash.lua32
-rw-r--r--crawl-ref/source/lua/wield.lua47
10 files changed, 1025 insertions, 0 deletions
diff --git a/crawl-ref/source/lua/base.lua b/crawl-ref/source/lua/base.lua
new file mode 100644
index 0000000000..db7b15afda
--- /dev/null
+++ b/crawl-ref/source/lua/base.lua
@@ -0,0 +1,74 @@
+---------------------------------------------------------------------------
+-- base.lua:
+-- Base Lua definitions that other Lua scripts rely on.
+-- NOTE: Other Lua scripts may demonstrate buggy behaviour if you do
+-- not source this file. If you're using no Lua scripts at all, you
+-- needn't source base.lua.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/base.lua
+---------------------------------------------------------------------------
+
+-- Lua global data
+chk_interrupt_macro = { }
+chk_interrupt_activity = { }
+chk_lua_option = { }
+
+-- Push into this table, rather than indexing into it.
+chk_lua_save = { }
+
+function c_save(file)
+ if not chk_lua_save then
+ return
+ end
+
+ for i, fn in ipairs(chk_lua_save) do
+ fn(file)
+ end
+end
+
+-- This function returns true to tell Crawl not to process the option further
+function c_process_lua_option(key, val)
+ if chk_lua_option and chk_lua_option[key] then
+ return chk_lua_option[key](key, val)
+ end
+ return false
+end
+
+function c_interrupt_macro(iname, cause, extra)
+ -- If some joker undefined the table, stop all macros
+ if not chk_interrupt_macro then
+ return true
+ end
+
+ -- Maybe we don't know what macro is running? Kill it to be safe
+ -- We also kill macros that don't add an entry to chk_interrupt_macro.
+ if not c_macro_name or not chk_interrupt_macro[c_macro_name] then
+ return true
+ end
+
+ return chk_interrupt_macro[c_macro_name](iname, cause, extra)
+end
+
+-- Notice that c_interrupt_activity defaults to *false* whereas
+-- c_interrupt_macro defaults to *true*. This is because "false" really just
+-- means "go ahead and use the default logic to kill this activity" here,
+-- whereas false is interpreted as "no, don't stop this macro" for
+-- c_interrupt_macro.
+--
+-- If c_interrupt_activity, or one of the individual hooks wants to ensure that
+-- the activity continues, it must return *nil* (make sure you know what you're
+-- doing when you return nil!).
+function c_interrupt_activity(aname, iname, cause, extra)
+ -- If some joker undefined the table, bail out
+ if not chk_interrupt_activity then
+ return false
+ end
+
+ -- No activity name? Bail!
+ if not aname or not chk_interrupt_activity[aname] then
+ return false
+ end
+
+ return chk_interrupt_activity[aname](iname, cause, extra)
+end
diff --git a/crawl-ref/source/lua/chnkdata.lua b/crawl-ref/source/lua/chnkdata.lua
new file mode 100644
index 0000000000..60f098c96b
--- /dev/null
+++ b/crawl-ref/source/lua/chnkdata.lua
@@ -0,0 +1,35 @@
+-- SPOILER WARNING
+--
+-- This file contains spoiler information. Do not read or use this file if you
+-- don't want to be spoiled. Further, the Lua code in this file may use this
+-- spoily information to take actions on your behalf. If you don't want
+-- automatic exploitation of spoilers, don't use this.
+--
+---------------------------------------------------------------------------
+-- chnkdata.lua:
+-- Raw data on chunks of meat, auto-extracted from mon-data.h.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/chnkdata.lua
+--
+-- This file has no directly usable functions, but is needed by gourmand.lua
+-- and enables auto_eat_chunks in eat.lua.
+---------------------------------------------------------------------------
+
+sc_cont = {"program bug","ettin","goblin","jackal","manticore","orc","ugly thing","two-headed ogre","hobgoblin","ogre","troll","orc warrior","orc wizard","orc knight","minotaur","beast","iron devil","orc sorcerer","","","hell knight","necromancer","wizard","orc priest","orc high priest","human","gnoll","earth elemental","fire elemental","air elemental","Ice Fiend","Shadow Fiend","spectral warrior","pulsating lump","rock troll","stone giant","flayed ghost","insubstantial wisp","vapour","ogre-mage","dancing weapon","elf","war dog","grey rat","giant mosquito","fire giant","frost giant","firedrake","deep troll","giant blowfly","swamp dragon","swamp drake","hill giant","giant cockroach","white imp","lemure","ufetubus","manes","midge","neqoxec","hellwing","smoke demon","ynoxinul","Executioner","Blue Death","Balrug","Cacodemon","demonic crawler","sun demon","shadow imp","shadow wraith","Mnoleg","Lom Lobon","Cerebov","Gloorx Vloq","orc warlord","deep elf soldier","deep elf fighter","deep elf knight","deep elf mage","deep elf summoner","deep elf conjurer","deep elf priest","deep elf high priest","deep elf demonologist","deep elf annihilator","deep elf sorcerer","deep elf death mage","Terence","Jessica","Ijyb","Sigmund","Blork the orc","Edmund","Psyche","Erolcha","Donald","Urug","Michael","Joseph","Snorg","Erica","Josephine","Harold","Norbert","Jozef","Agnes","Maud","Louise","Francis","Frances","Rupert","Wayne","Duane","Xtahua","Norris","Adolf","Margery","Boris","Geryon","Dispater","Asmodeus","Antaeus","Ereshkigal","vault guard","Killer Klown","ball lightning","orb of fire","boggart","quicksilver dragon","iron dragon","skeletal warrior","","&","warg","","","komodo dragon"}
+sc_pois = {"killer bee","killer bee larva","quasit","scorpion","yellow wasp","giant beetle","kobold","queen bee","kobold demonologist","big kobold","wolf spider","brain worm","boulder beetle","giant mite","hydra","mottled dragon","brown snake","death yak","bumblebee","redback","spiny worm","golden dragon","elephant slug","green rat","orange rat","black snake","giant centipede","iron troll","naga","yellow snake","red wasp","soldier ant","queen ant","ant larva","spiny frog","orange demon","Green Death","giant amoeba","giant slug","giant snail","boring beetle","naga mage","naga warrior","brown ooze","azure jelly","death ooze","acid blob","ooze","shining eye","greater naga","eye of devastation","gila monster"}
+sc_hcl = {"necrophage","ghoul"}
+sc_mut = {"guardian naga","eye of draining","giant orange brain","great orb of eyes","glowing shapeshifter","shapeshifter","very ugly thing"}
+
+function sc_to_hash(list)
+ local hash = { }
+ for _, i in ipairs(list) do
+ hash[i] = true
+ end
+ return hash
+end
+
+sc_cont = sc_to_hash( sc_cont )
+sc_pois = sc_to_hash( sc_pois )
+sc_hcl = sc_to_hash( sc_hcl )
+sc_mut = sc_to_hash( sc_mut )
diff --git a/crawl-ref/source/lua/eat.lua b/crawl-ref/source/lua/eat.lua
new file mode 100644
index 0000000000..2e5bda2432
--- /dev/null
+++ b/crawl-ref/source/lua/eat.lua
@@ -0,0 +1,97 @@
+---------------------------------------------------------------------------
+-- eat.lua:
+-- Prompts to eat chunks from inventory.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/eat.lua
+--
+-- See c_eat in this file if you want to tweak eating behaviour further.
+---------------------------------------------------------------------------
+function prompt_eat(i)
+ local iname = item.name(i, "a")
+ if item.quantity(i) > 1 then
+ iname = "one of " .. iname
+ end
+ crawl.mpr("Eat " .. iname .. "?", "prompt")
+
+ local res
+ res = crawl.getch()
+ if res == 27 then
+ res = "escape"
+ elseif res < 32 or res > 127 then
+ res = ""
+ else
+ res = string.lower(string.format("%c", res))
+ end
+ return res
+end
+
+function is_chunk_safe(chunk)
+ local rot = food.rotting(chunk)
+ local race = you.race()
+
+ -- Check if the user has sourced safechunk.lua and chnkdata.lua
+ if not (sc_cont and sc_pois and sc_hcl and sc_mut and sc_safechunk) then
+ return false
+ end
+
+ local cname = item.name(chunk)
+ local mon
+ _, _, mon = string.find(cname, "chunk of (.+) flesh")
+
+ return sc_safechunk(rot, race, mon)
+end
+
+-- Called by Crawl. Note that once Crawl sees a c_eat function, it bypasses the
+-- built-in (e)at command altogether.
+--
+function c_eat(floor, inv)
+ -- To enable auto_eat_chunks, you also need to source chnkdata.lua and
+ -- safechunk.lua. WARNING: These files contain spoily information.
+ local auto_eat_chunks = options.auto_eat_chunks == "yes" or
+ options.auto_eat_chunks == "true"
+
+ if auto_eat_chunks then
+ local all = { }
+ for _, it in ipairs(floor) do table.insert(all, it) end
+ for _, it in ipairs(inv) do table.insert(all, it) end
+
+ for _, it in ipairs(all) do
+ if food.ischunk(it) and food.can_eat(it) and is_chunk_safe(it) then
+ local iname = item.name(it, "a")
+ if item.quantity(it) > 1 then
+ iname = "one of " .. iname
+ end
+ crawl.mpr("Eating " .. iname)
+ food.eat(it)
+ return
+ end
+ end
+ end
+
+ -- Prompt to eat chunks off the floor. Returns true if the player chose
+ -- to eat a chunk.
+ if food.prompt_floor() then
+ return
+ end
+
+ for i, it in ipairs(inv) do
+ -- If we have chunks in inventory that the player can eat, prompt.
+ if food.ischunk(it) and food.can_eat(it) then
+ local answer = prompt_eat(it)
+ if answer == "q" then
+ break
+ end
+ if answer == "escape" then
+ return
+ end
+ if answer == "y" then
+ food.eat(it)
+ return
+ end
+ end
+ end
+
+ -- Allow the player to choose a snack from inventory
+ food.prompt_inventory()
+end
diff --git a/crawl-ref/source/lua/gearset.lua b/crawl-ref/source/lua/gearset.lua
new file mode 100644
index 0000000000..494ebd65ff
--- /dev/null
+++ b/crawl-ref/source/lua/gearset.lua
@@ -0,0 +1,206 @@
+------------------------------------------------------------------------
+-- gearset.lua: (requires base.lua)
+-- Allows for quick switching between two sets of equipment.
+--
+-- IMPORTANT
+-- This Lua script remembers only the *inventory slots* of the gear you're
+-- wearing (it could remember item names, but they're prone to change based on
+-- identification, enchantment and curse status). If you swap around items in
+-- your inventory, the script may attempt to wear odd items (this will not kill
+-- the game, but it can be disconcerting if you're not expecting it).
+--
+-- To use this, source this file in your init.txt:
+-- lua_file = lua/gearset.lua
+--
+-- Then start Crawl and create two macros:
+-- 1. Any macro input key (say F2), set macro action to "===rememberkit"
+-- 2. Any macro input key (say F3), set macro action to "===swapkit"
+-- It helps to save your macros at this point. :-)
+--
+-- You can now hit F2 to remember the equipment you're wearing. Once you've
+-- defined two sets of equipment, hit F3 to swap between them.
+--
+-- You can also just define one set of equipment; in this case, every time
+-- you hit F3 (swapkit), the macro makes sure you're wearing all the items in
+-- that set (and only the items in that set). This is handy for transmuters who
+-- need to kit up after a transform ends.
+------------------------------------------------------------------------
+function scan_kit()
+ local kit = { }
+ for i = 9, 0, -1 do
+ local it = item.equipped_at(i)
+ if it then
+ table.insert(kit, item.slot(it))
+ end
+ end
+ return kit
+end
+
+function kit_items(kit)
+ local items = { }
+ local inv = item.inventory()
+
+ for _, slot in ipairs(kit) do
+ for _, it in ipairs(inv) do
+ if slot == item.slot(it) then
+ table.insert(items, it)
+ end
+ end
+ end
+ return items
+end
+
+function getkey(vkeys)
+ local key
+
+ while true do
+ key = crawl.getch()
+ if key == 27 then return "escape" end
+
+ if key > 31 and key < 127 then
+ local c = string.lower(string.char(key))
+ if string.find(vkeys, c) then
+ return c
+ end
+ end
+ end
+end
+
+-- Macroable
+function rememberkit()
+ local kit = scan_kit()
+ crawl.mpr("Is this (T)ravel or (B)attle kit? ", "prompt")
+ local answer = getkey("tb")
+ crawl.mesclr()
+ if answer == "escape" then
+ return false
+ end
+
+ if answer == 't' then
+ g_kit_travel = kit
+ else
+ g_kit_battle = kit
+ end
+
+ return false
+end
+
+function write_array(f, arr, aname)
+ file.write(f, aname .. " = { ")
+ for i, v in ipairs(arr) do
+ file.write(f, v .. ", ")
+ end
+ file.write(f, "}\n")
+end
+
+function gearset_save(file)
+ if g_kit_travel then
+ write_array(file, g_kit_travel, "g_kit_travel")
+ end
+ if g_kit_battle then
+ write_array(file, g_kit_battle, "g_kit_battle")
+ end
+end
+
+function matchkit(kit1, kit2)
+ local matches = 0
+ if not kit2 then
+ -- Return a positive match for an empty gearset so that swapkit
+ -- switches to the *other* gearset. :-)
+ return 500
+ end
+ for _, v1 in ipairs(kit1) do
+ for _, v2 in ipairs(kit2) do
+ if v1 == v2 then
+ matches = matches + 1
+ end
+ end
+ end
+ return matches
+end
+
+function wear(it)
+ local _, eqt = item.equip_type(it)
+ if eqt == "Weapon" then
+ return item.wield(it)
+ elseif eqt == "Amulet" or eqt == "Ring" then
+ return item.puton(it)
+ elseif eqt ~= "" then
+ return item.wear(it)
+ else
+ return false
+ end
+end
+
+function wearkit(kit)
+ -- Remove all items not in the kit, then wear all items in the kit
+ local currkit = scan_kit()
+ local toremove = { }
+ local noop = true
+
+ for _, v in ipairs(currkit) do
+ local found = false
+ for _, v1 in ipairs(kit) do
+ if v == v1 then
+ found = true
+ break
+ end
+ end
+ if not found then
+ table.insert(toremove, v)
+ end
+ end
+
+ local remitems = kit_items(toremove)
+ for _, it in ipairs(remitems) do
+ noop = false
+ if not item.remove(it) then
+ coroutine.yield(false)
+ end
+ coroutine.yield(true)
+ end
+
+ local kitems = kit_items(kit)
+
+ for _, it in ipairs(kitems) do
+ if not item.worn(it) then
+ noop = false
+ if not wear(it) then
+ coroutine.yield(false)
+ end
+ coroutine.yield(true)
+ end
+ end
+
+ if noop then
+ crawl.mpr("Nothing to do.")
+ end
+end
+
+-- Macroable
+function swapkit()
+ if not g_kit_battle and not g_kit_travel then
+ crawl.mpr("You need to define a gear set first")
+ return false
+ end
+
+ local kit = scan_kit()
+ if matchkit(kit, g_kit_travel) < matchkit(kit, g_kit_battle) then
+ crawl.mpr("Switching to travel kit")
+ wearkit(g_kit_travel)
+ else
+ crawl.mpr("Switching to battle kit")
+ wearkit(g_kit_battle)
+ end
+end
+
+function swapkit_interrupt_macro(interrupt_name)
+ return interrupt_name == "hp_loss" or interrupt_name == "stat" or
+ interrupt_name == "monster" or interrupt_name == "force"
+end
+
+-- Add a macro interrupt hook so that we don't get stopped by any old interrupt
+chk_interrupt_macro.swapkit = swapkit_interrupt_macro
+
+-- Add ourselves in the Lua save chain
+table.insert(chk_lua_save, gearset_save)
diff --git a/crawl-ref/source/lua/gourmand.lua b/crawl-ref/source/lua/gourmand.lua
new file mode 100644
index 0000000000..a730438419
--- /dev/null
+++ b/crawl-ref/source/lua/gourmand.lua
@@ -0,0 +1,145 @@
+-- SPOILER WARNING
+--
+-- This file contains spoiler information. Do not read or use this file if you
+-- don't want to be spoiled. Further, the Lua code in this file may use this
+-- spoily information to take actions on your behalf. If you don't want
+-- automatic exploitation of spoilers, don't use this.
+--
+---------------------------------------------------------------------------
+-- gourmand.lua: (requires eat.lua, chnkdata.lua, and safechunk.lua)
+--
+-- Eats all available chunks on current square and inventory, swapping to an
+-- identified amulet of the gourmand if necessary, with no prompts. Note that
+-- it relies on spoiler information to identify chunks it can eat without
+-- prompting the user.
+--
+-- This is a Lua macro, so the action will be interrupted by the hp/stat loss,
+-- or monsters.
+--
+-- To use this, add these line to your init.txt:
+-- lua_file = lua/gourmand.lua
+--
+-- And macro the "eat_gourmand" function to a key.
+---------------------------------------------------------------------------
+-- Macroable
+function eat_gourmand()
+ local race = you.race()
+ local all = { }
+ for _, it in ipairs(you.floor_items()) do table.insert(all, it) end
+ for _, it in ipairs(item.inventory()) do table.insert(all, it) end
+
+ local chunks = { }
+ local needgourmand = false
+ local oneedible = false
+
+ for _, itym in ipairs(all) do
+ if food.ischunk(itym) then
+ table.insert(chunks, itym)
+ end
+ end
+
+ for _, itym in ipairs(chunks) do
+ local rot = food.rotting(itym)
+ local mon = chunkmon(itym)
+
+ if food.can_eat(itym) and sc_safechunk and
+ sc_safechunk(rot, race, mon) then
+ oneedible = true
+ end
+
+ -- If we can't eat it now, but we could eat it if hungry, a gourmand
+ -- switch would be nice.
+ if not food.can_eat(itym) and food.can_eat(itym, false) then
+ needgourmand = true
+ end
+
+ if sc_safechunk and not sc_safechunk(rot, race, mon)
+ and sc_safechunk(false, race, mon)
+ then
+ needgourmand = true
+ end
+ end
+
+ if table.getn(chunks) == 0 then
+ crawl.mpr("No chunks to eat.")
+ return
+ end
+
+ local amuremoved
+ if needgourmand and not oneedible then
+ amuremoved = switch_amulet("gourmand")
+ end
+
+ for _, ch in ipairs(chunks) do
+ if food.can_eat(ch) and is_chunk_safe(ch) then
+ while item.quantity(ch) > 0 do
+ if food.eat(ch) then
+ coroutine.yield(true)
+ else
+ break
+ end
+ end
+ end
+ end
+
+ if amuremoved then
+ switch_amulet(amuremoved)
+ end
+end
+
+function chunkmon(chunk)
+ local cname = item.name(chunk)
+ local mon
+ _, _, mon = string.find(cname, "chunk of (.+) flesh")
+ return mon
+end
+
+function switch_amulet(amu)
+ local towear
+ if type(amu) == "string" then
+ for _, it in ipairs(item.inventory()) do
+ if item.class(it, true) == "jewelry"
+ and item.subtype(it) == amu
+ and not item.cursed(it) then
+ towear = item.slot(it)
+ break
+ end
+ end
+
+ if not towear then
+ crawl.mpr("Can't find suitable amulet: " .. amu)
+ coroutine.yield(false)
+ end
+ else
+ towear = amu
+ end
+
+ local curramu = item.equipped_at("amulet")
+ if curramu and item.cursed(curramu) then
+ crawl.mpr("Can't swap out cursed amulet!")
+ coroutine.yield(false)
+ end
+
+ local wearitem = item.inslot(towear)
+
+ if curramu then
+ if not item.remove(curramu) then
+ coroutine.yield(false)
+ end
+
+ -- Yield, so that a turn can pass and we can take another action.
+ coroutine.yield(true)
+ end
+
+ if not item.puton(wearitem) then
+ coroutine.yield(false)
+ end
+
+ coroutine.yield(true)
+ return curramu and item.slot(curramu)
+end
+
+function c_interrupt_macro(interrupt_name)
+ return interrupt_name == "hp_loss" or interrupt_name == "stat" or
+ interrupt_name == "monster" or interrupt_name == "force"
+end
diff --git a/crawl-ref/source/lua/kills.lua b/crawl-ref/source/lua/kills.lua
new file mode 100644
index 0000000000..b84b1f93c0
--- /dev/null
+++ b/crawl-ref/source/lua/kills.lua
@@ -0,0 +1,240 @@
+------------------------------------------------------------------
+-- kills.lua:
+-- Generates fancier vanquished creatures lists.
+--
+-- List ideas courtesy Jukka Kuusisto and Erik Piper.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/kills.lua
+--
+-- Take a look at the c_kill_list function - that's where to start if you
+-- want to customize things.
+------------------------------------------------------------------
+
+function kill_filter(a, condition)
+ local t = { }
+ for i, k in ipairs(a) do
+ if condition(k) then
+ table.insert(t, k)
+ end
+ end
+ return t
+end
+
+function show_sorted_list(list, baseindent, sortfn)
+ baseindent = baseindent or " "
+ sortfn = sortfn or
+ function (x, y)
+ return kills.exp(x) > kills.exp(y) or
+ (kills.exp(x) == kills.exp(y) and
+ kills.base_name(x) < kills.base_name(y))
+ end
+ table.sort(list, sortfn)
+ for i, k in ipairs(list) do
+ kills.rawwrite(baseindent .. " " .. kills.desc(k))
+ end
+ kills.rawwrite(baseindent .. kills.summary(list))
+end
+
+function group_kills(a, namemap, keys, selector)
+ local count = 0
+ for i, key in ipairs(keys) do
+ local selected = kill_filter(a,
+ function (k)
+ return selector(key, k)
+ end
+ )
+ if table.getn(selected) > 0 then
+ if count > 0 then
+ kills.rawwrite("")
+ end
+ count = count + 1
+ kills.rawwrite(" " .. namemap[key])
+ show_sorted_list(selected)
+ end
+ end
+end
+
+function holiness_list(a)
+ local holies = {
+ strange = "Strange Monsters",
+ unique = "Uniques",
+ holy = "Holy Monsters",
+ natural = "Natural Monsters",
+ undead = "Undead Monsters",
+ demonic = "Demonic Monsters",
+ nonliving = "Non-Living Monsters",
+ plant = "Plants",
+ }
+ local holysort = { "strange", "unique",
+ "natural", "nonliving",
+ "undead", "demonic", "plant" }
+ kills.rawwrite(" Monster Nature")
+ group_kills( a, holies, holysort,
+ function ( key, kill )
+ return (kills.holiness(kill) == key and not kills.isunique(kill))
+ or
+ (key == "unique" and kills.isunique(kill))
+ end
+ )
+ kills.rawwrite(" " .. kills.summary(a))
+end
+
+function count_list(a, ascending)
+ kills.rawwrite(" Ascending Order")
+ show_sorted_list(a,
+ " ",
+ function (x, y)
+ if ascending then
+ return kills.nkills(x) < kills.nkills(y)
+ or (kills.nkills(x) == kills.nkills(y) and
+ kills.exp(x) > kills.exp(y))
+ else
+ return kills.nkill(x) > kills.nkills(y)
+ or (kills.nkills(x) == kills.nkills(y) and
+ kills.exp(x) > kills.exp(y))
+ end
+ end
+ )
+end
+
+function symbol_list(a)
+ -- Create a list of monster characters and sort it
+ local allsyms = { }
+ for i, k in ipairs(a) do
+ local ksym = kills.symbol(k)
+ allsyms[ kills.symbol(k) ] = true
+ end
+
+ local sortedsyms = { }
+ for sym, dud in pairs(allsyms) do table.insert(sortedsyms, sym) end
+ table.sort(sortedsyms)
+
+ kills.rawwrite(" Monster Symbol")
+ for i, sym in ipairs(sortedsyms) do
+ if i > 1 then
+ kills.rawwrite("")
+ end
+
+ local symlist = kill_filter(a,
+ function (k)
+ return kills.symbol(k) == sym
+ end
+ )
+ kills.rawwrite(" All '" .. sym .. "'")
+ show_sorted_list(symlist)
+ end
+
+ kills.rawwrite(" " .. kills.summary(a))
+end
+
+function classic_list(title, a, suffix)
+ if table.getn(a) == 0 then return end
+ kills.rawwrite(title)
+ for i, k in ipairs(a) do
+ kills.rawwrite(" " .. kills.desc(k))
+ end
+ kills.rawwrite(" " .. kills.summary(a))
+ if suffix then
+ kills.rawwrite(suffix)
+ end
+end
+
+function separator()
+ kills.rawwrite("----------------------------------------\n")
+end
+
+function newline()
+ kills.rawwrite("")
+end
+
+-----------------------------------------------------------------------------
+-- This is the function that Crawl calls when it wants to dump the kill list
+-- The parameter "a" is the list (Lua table) of kills, initially sorted in
+-- descending order of experience. Kill entries must be inspected using
+-- kills.foo(ke).
+--
+-- NOTE: Comment out the kill lists that you don't like!
+--
+-- As of 20060224, the kill list is divided into three:
+-- * Direct player kills
+-- * Monsters killed by friendlies
+-- * Monsters killed by other things (traps, etc.)
+--
+-- For each category, the game calls c_kill_list, with a table of killed
+-- monsters, and the killer (who). The killer will be nil for direct player
+-- kills, and a string ("collateral kills", "others") otherwise.
+--
+-- c_kill_list will not be called for a category if the category contains no
+-- kills.
+--
+-- After all categories, c_kill_list will be called with no arguments to
+-- indicate the end of the kill listing.
+
+function c_kill_list(a, who, needsep)
+ -- If there aren't any kills yet, bail out
+ if not a or table.getn(a) == 0 then
+ if not a and who and who ~= "" then
+ -- This is the grand total
+ separator()
+ kills.rawwrite(who)
+ end
+ return
+ end
+
+ if needsep then
+ separator()
+ end
+
+ local title = "Vanquished Creatures"
+ if who then
+ title = title .. " (" .. who .. ")"
+ end
+
+ kills.rawwrite(title)
+
+ -- Group kills by monster symbol
+ symbol_list(a)
+ newline()
+
+ -- Group by holiness
+ holiness_list(a)
+ newline()
+
+ -- Sort by kill count (ascending).
+ count_list(a, true)
+ newline()
+
+ -- Classic list without zombies and skeletons
+ -- Commented-out - this is a boring list.
+ --[[
+ -- First re-sort into descending order of exp, since
+ -- count_list has re-sorted the array.
+ table.sort(a, function (x, y)
+ return kills.exp(x) > kills.exp(y)
+ end);
+
+ -- Filter out zombies and skeletons and display the rest
+ classic_list(
+ " Kills minus zombies and skeletons",
+ kill_filter(a,
+ function (k)
+ return kills.modifier(k) ~= "zombie" and
+ kills.modifier(k) ~= "skeleton"
+ end
+ ))
+
+ separator()
+ --]]
+
+ -- Show only monsters with 3 digit kill count
+ classic_list(
+ " The 3-digit Club",
+ kill_filter(a,
+ function (k)
+ return kills.nkills(k) > 99
+ end
+ ),
+ "")
+
+end
diff --git a/crawl-ref/source/lua/runrest.lua b/crawl-ref/source/lua/runrest.lua
new file mode 100644
index 0000000000..6f5536506e
--- /dev/null
+++ b/crawl-ref/source/lua/runrest.lua
@@ -0,0 +1,108 @@
+---------------------------------------------------------------------------
+-- runrest.lua: (requires base.lua)
+-- Controls shift-running and resting stop conditions.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/runrest.lua
+--
+-- What it does:
+--
+-- * Any message in runrest_ignore_message will *not* stop your run.
+-- * Poison damage of x will be ignored if you have at least y hp if you've
+-- defined a runrest_ignore_poison = x:y option.
+--
+-- IMPORTANT: You must define runrest_ options *after* sourcing runrest.lua.
+---------------------------------------------------------------------------
+
+g_rr_ignored = { }
+
+chk_interrupt_activity.run = function (iname, cause, extra)
+ if not rr_check_params() then
+ return false
+ end
+
+ if iname == 'message' then
+ return rr_handle_message(cause, extra)
+ end
+
+ if iname == 'hp_loss' then
+ return rr_handle_hploss(cause, extra)
+ end
+
+ return false
+end
+
+function rr_handle_message(cause, extra)
+ local ch, mess = rr_split_channel(cause)
+ for _, m in ipairs(g_rr_ignored) do
+ if m:matches(mess, ch) then
+ return nil
+ end
+ end
+ return false
+end
+
+function rr_split_channel(s)
+ local chi = string.find(s, ':')
+ local channel = -1
+ if chi and chi > 1 then
+ local chstr = string.sub(s, 1, chi - 1)
+ channel = crawl.msgch_num(chstr)
+ end
+
+ local sor = s
+ if chi then
+ s = string.sub(s, chi + 1, -1)
+ end
+
+ return channel, s
+end
+
+function rr_handle_hploss(hplost, source)
+ -- source == 1 for poisoning
+ if not g_rr_yhpmin or not g_rr_hplmax or source ~= 1 then
+ return false
+ end
+
+ -- If the hp lost is smaller than configured, and you have more than the
+ -- minimum health, ignore this poison event.
+ if hplost <= g_rr_hplmax and you.hp() >= g_rr_yhpmin then
+ return nil
+ end
+
+ return false
+end
+
+function rr_check_params()
+ if g_rrim ~= options.runrest_ignore_message then
+ g_rrim = options.runrest_ignore_message
+ rr_add_messages(nil, g_rrim)
+ end
+
+ if ( not g_rr_hplmax or not g_rr_yhpmin )
+ and options.runrest_ignore_poison
+ then
+ local opt = options.runrest_ignore_poison
+ local hpl, hpm
+ _, _, hpl, hpm = string.find(opt, "(%d+)%s*:%s*(%d+)")
+ if hpl and hpm then
+ g_rr_hplmax = tonumber(hpl)
+ g_rr_yhpmin = tonumber(hpm)
+ end
+ end
+ return true
+end
+
+function rr_add_message(s)
+ local channel, str = rr_split_channel(s)
+ table.insert( g_rr_ignored, crawl.message_filter( str, channel ) )
+end
+
+function rr_add_messages(key, value)
+ local segs = crawl.split(value, ',')
+ for _, s in ipairs(segs) do
+ rr_add_message(s)
+ end
+end
+
+chk_lua_option.runrest_ignore_message = rr_add_messages
diff --git a/crawl-ref/source/lua/safechunk.lua b/crawl-ref/source/lua/safechunk.lua
new file mode 100644
index 0000000000..cd0c9f036d
--- /dev/null
+++ b/crawl-ref/source/lua/safechunk.lua
@@ -0,0 +1,41 @@
+-- SPOILER WARNING
+--
+-- This file contains spoiler information. Do not read or use this file if you
+-- don't want to be spoiled. Further, the Lua code in this file may use this
+-- spoily information to take actions on your behalf. If you don't want
+-- automatic exploitation of spoilers, don't use this.
+--
+---------------------------------------------------------------------------
+-- safechunk.lua:
+-- Determines whether a chunk of meat is safe for your character to eat.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/safechunk.lua
+--
+-- This file has no directly usable functions, but is needed by gourmand.lua
+-- and enables auto_eat_chunks in eat.lua.
+---------------------------------------------------------------------------
+
+function sc_safechunk(rot, race, mon)
+ if race == "Ghoul" then
+ return true
+ end
+
+ if rot then
+ if race ~= "Kobold" and race ~= "Troll" and not you.gourmand() then
+ return false
+ end
+ end
+
+ if sc_pois[mon] and you.res_poison() > 0 then
+ return true
+ end
+
+ if sc_hcl[mon] or sc_mut[mon] then
+ return false
+ end
+
+ -- Only contaminated and clean chunks remain, in theory. We'll accept
+ -- either
+ return true
+end
diff --git a/crawl-ref/source/lua/stash.lua b/crawl-ref/source/lua/stash.lua
new file mode 100644
index 0000000000..461a504cf3
--- /dev/null
+++ b/crawl-ref/source/lua/stash.lua
@@ -0,0 +1,32 @@
+---------------------------------------------------------------------------
+-- stash.lua
+-- Annotates items for the stash-tracker.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/stash.lua
+--
+---------------------------------------------------------------------------
+
+-- Annotate items for searches
+function ch_stash_search_annotate_item(it)
+ local annot = ""
+
+ if item.artifact(it) then
+ annot = annot .. "{art} "
+ elseif item.branded(it) then
+ annot = annot .. "{ego} "
+ elseif item.class(it, true) == "book" then
+ annot = annot .. "{book} "
+ end
+
+ local skill = item.weap_skill(it)
+ if skill then
+ annot = annot .. "{" .. skill .. "} "
+ end
+
+ return annot
+end
+
+--- If you want dumps (.lst files) to be annotated, uncomment this line:
+-- ch_stash_dump_annotate_item = ch_stash_search_annotate_item
+
diff --git a/crawl-ref/source/lua/wield.lua b/crawl-ref/source/lua/wield.lua
new file mode 100644
index 0000000000..e4fe9951e3
--- /dev/null
+++ b/crawl-ref/source/lua/wield.lua
@@ -0,0 +1,47 @@
+---------------------------------------------------------------------------
+-- wield.lua
+-- Selects extra items to wield.
+--
+-- To use this, add this line to your init.txt:
+-- lua_file = lua/wield.lua
+---------------------------------------------------------------------------
+
+function make_hash(ls)
+ local h = { }
+ for _, i in ipairs(ls) do
+ h[i] = true
+ end
+ return h
+end
+
+function ch_item_wieldable(it)
+ -- We only need to check for unusual cases - basic matching is handled
+ -- by Crawl itself.
+ local spells = make_hash( you.spells() )
+
+ if spells["Bone Shards"]
+ and string.find( item.name(it, "a"), " skeleton" )
+ then
+ return true
+ end
+
+ if (spells["Sublimation of Blood"] or spells["Simulacrum"])
+ and food.ischunk(it)
+ then
+ return true
+ end
+
+ if spells["Sandblast"]
+ and string.find( item.name(it, "a"), " stones?$" )
+ then
+ return true
+ end
+
+ if spells["Sticks to Snakes"]
+ and string.find( item.name(it, "a"), " arrows?$" )
+ then
+ return true
+ end
+
+ return false
+end