# Crawl Init file # # On Unix systems (such as Mac OS X, Linux and the BSDs), you must copy # init.txt to ~/.crawlrc or ~/.crawl/init.txt as: # cp init.txt ~/.crawlrc # # Lines beginning with '#' are comments. The basic syntax is: # # field = value or field.subfield = value # # Only one specification is allowed per line. # # The terms are typically case-insensitive except in the fairly obvious # cases (the character's name and specifying files or directories when # on a system that has case-sensitive filenames). # # White space is stripped from the beginning and end of the line, as # well as immediately before and after the '='. If the option allows # multiple comma/semicolon-separated terms (such as # autopickup_exceptions), all whitespace around the separator is also # trimmed. All other whitespace is left intact. # # For descriptions concerning an option consult the file # options_guide.txt # in your /docs directory. Also note that the ordering of the options # is taken from that file; this is for presentational reasons only. # # Note that all boolean options (i.e. values of 'true' or 'false') have # their non-default value commented out. You can toggle these by just # uncommenting. # Options with several values, e.g. drop_mode = (multi | single), # usually have the first value as default. # # The options for monster glyphs and item colours don't matter for Tiles. # Similarily, tile options are ignored in console games. #begin lua { ---@diagnostic disable: lowercase-global rclogging = false function rclog(...) if rclogging or you.wizard() then local parts = {} for i = 1, select('#', ...) do local a = select(i, ...) -- if a function does not return anything, this casts the empty return to nil parts[i] = tostring(a) end out = table.concat(parts, " ") crawl.mpr(out) print(out) end end local function sendkeys(...) rclog("sending keys:", table.concat({ ... }, " ")) crawl.sendkeys(...) end ---@param a string ---@param b string function swap_letter_slots(a, b) it1, it2 = get_item(a), get_item(b) if not it1 or not it2 then rclog("swap_letter_slots passed invalid item(s):", a, b) end items.swap_gear_slots(it1.slot, it2.slot) end ---@param i string|integer|Item ---@return Item? function get_item(i) if not i then return end if type(i) == "number" then return items.inslot(i) end ---@diagnostic disable-next-line: param-type-mismatch return items.equipped_at(i) or items.inslot(items.letter_to_index(i)) end ---@param i string|Item function item_info(i) it = get_item(i) if not it then rclog("item_info was passed", i, "which is not an item") return end rclog("name:", it.name()) rclog("class:", it.class()) rclog("subtype:", it.subtype(false)) rclog("equip type:", it.equip_type) end function c_answer_prompt(prompt) --#todo change this to be a blacklist of summoned allies instead of a whitelist of constructs local constructs = { "spellspark", "lightning spire", "blazeheart" } for _, ally in ipairs(constructs) do if prompt:find("your " .. ally) then return true end end end function toggle_ring() local x_ring = items.inslot(items.letter_to_index("x")) local y_ring = items.inslot(items.letter_to_index("y")) if not x_ring or not y_ring then crawl.mpr("x and y slots not assigned to items") return end if not x_ring.equip_type == "ring" or not y_ring.equip_type == "ring" then crawl.mpr("x and y slots not assigned to rings") return end if not y_ring.equipped and not x_ring.equipped then sendkeys("Px") return end current_ring = x_ring.equipped and "x" or "y" opposite_ring = current_ring == "x" and "y" or "x" sendkeys(("P" .. current_ring .. "P" .. opposite_ring)) end local function ratio(val1, val2) return val1 / val2 end function smart_rest() -- target = tonumber(crawl.getopt("rest_wait_percent")) / 100 or 1 target = 0.6 local hp_ratio = ratio(you.hp()) local mp_ratio = ratio(you.mp()) crawl.mpr("hp is at " .. hp_ratio .. ", mp is at " .. mp_ratio) if (hp_ratio < target) or (mp_ratio < target) then crawl.mpr("resting before explore.") crawl.do_commands({ "CMD_REST", "CMD_EXPLORE_NO_REST" }) else crawl.do_commands({ "CMD_EXPLORE_NO_REST" }) end end function explore_no_rest() crawl.do_commands({ "CMD_EXPLORE_NO_REST" }) end function short_explore() if view.feature_at(0,0) == "Enter_shop" then if c_persist.current_tile_visited then rclog("current shop already visited") c_persist.current_tile_visited = nil else rclog("current shop unvisited") c_persist.current_tile_visited = true return sendkeys(">") end end for _,it in ipairs(you.floor_items()) do if not it.is_useless then return explore_no_rest() end end sendkeys("Xo", 13) end local function train(skill, target, focus) focus = focus and 2 or 1 target = target or you.get_training_target(skill) or 0 you.set_training_target(skill, target) you.train_skill(skill, focus) end -- values are number of (desirable) starting spells local casters = { ["Air Elementalist"] = 3, ["Fire Elementalist"] = 4, ["Summoner"] = 4, ["Conjurer"] = 3, ["Necromancer"] = 4, ["Hedge Wizard"] = 4 } -- reaver spells are not great local magic_users = { ["Hexslinger"] = 5, ["Warper"] = 5, ["Enchanter"] = 4, ["Reaver"] = 1 } w_skills = { "Maces & Flails", "Axes", "Polearms", "Staves", "Unarmed Combat", "Long Blades", "Short Blades", "Ranged Weapons" } local function best_weapon_skill() local max_name, max_val = nil, 0 for i, skill in ipairs(w_skills) do if you.skill(skill) > max_val then max_name = skill max_val = you.skill(skill) end end return max_name, max_val end local all_skills = { ["Fighting"] = true, ["Short Blades"] = true, ["Long Blades"] = true, ["Axes"] = true, ["Maces & Flails"] = true, ["Polearms"] = true, ["Staves"] = true, ["Ranged Weapons"] = true, ["Throwing"] = true, ["Armour"] = true, ["Dodging"] = true, ["Stealth"] = true, ["Shields"] = true, ["Unarmed Combat"] = true, ["Spellcasting"] = true, ["Conjurations"] = true, ["Hexes"] = true, ["Summonings"] = true, ["Necromancy"] = true, ["Translocations"] = true, ["Forgecraft"] = true, ["Fire Magic"] = true, ["Ice Magic"] = true, ["Air Magic"] = true, ["Earth Magic"] = true, ["Alchemy"] = true, ["Invocations"] = true, ["Evocations"] = true, ["Shapeshifting"] = true, }; skill_plans = { ["Tengu"] = { base = { ["Dodging"] = 4, ["Stealth"] = 3, }, ["Air Elementalist"] = { { ["Conjurations_f"] = 3, }, { ["Conjurations_f"] = 8, ["Air Magic_f"] = 10, ["Spellcasting"] = 6, ["Stealth"] = 4, ["Dodging"] = 5 }, }, ["Summoner"] = { { ["Spellcasting"] = 5, ["Summonings_f"] = 10, }, [2] = function() if spells.memorised("Summon Seismosaurus Egg") and spells.fail("Summon Seismosaurus Egg") > 6 then train("Summonings", 12) train("Earth Magic", 4, true) train("Fighting", 4) -- gotta fight to protect the egg :) train("Dodging", 5) return true end end }, ["Gladiator"] = { { ["Throwing"] = 4, ["Dodging"] = 6, ["Fighting"] = 5, }, }, ["Shapeshifter"] = { { ["Fighting"] = 3, ["Dodging"] = 3, }, { ["Unarmed Combat"] = 11, }, { ["Shapeshifting"] = 6 }, }, ["Reaver"] = { { ["Necromancy"] = 2, ["Stealth"] = 0, ["Dodging"] = 3 }, { ["Fighting"] = 4 }, }, ["Hedge Wizard"] = { { ["Short Blades_f"] = 2, ["Dodging"] = 0, ["Stealth"] = 0, }, { ["Necromancy"] = 3, }, { ["Air Magic_f"] = 7, ["Alchemy"] = 2, ["Conjurations"] = 7, }, { ["Fighting"] = 4, ["Dodging"] = 4, ["Stealth"] = 4, }, }, }, ["Octopode"] = { base = { ["Stealth"] = 10 }, }, }; ---@param s string ---@param t number local function train_plan_skill(s, t) local skill, focused if s:find("_f") then skill, focused = s:sub(1, -3), 2 else skill, focused = s, 1 end if not all_skills[skill] then error("plan contains invalid skill: " .. skill) end if t ~= 0 then if you.skill(skill) < t then you.train_skill(skill, focused) you.set_training_target(skill, t) return true else rclog("skill", s, "already at target") end else rclog("disabling skill", s) you.train_skill(skill, 0) end return false end ---@param source table ---@param dest table? function shallowcopy(source, dest) if not source then return {} end local copy = dest or {} for k, v in pairs(source) do copy[k] = v end return copy end local function apply_skill_plan() local sp_plans = skill_plans[you.species()] if not sp_plans then return false end local bg_plans = sp_plans[you.class()] if not bg_plans then return false end ---@type table local plan = shallowcopy(sp_plans.base) for i, next_plan in ipairs(bg_plans) do if type(next_plan) == "table" then shallowcopy(next_plan, plan) local applied = false for s, t in pairs(plan) do rclog("lookg at training for ", s, "t is", t) if train_plan_skill(s, t) then applied = true end end if applied then rclog("applied step", i) return true else rclog("skipped step", i) end else if next_plan() then return true end end end rclog("plan exhausted") return false end function toggle_plans() if c_persist.plans_disabled then if crawl.yesno("reenable skill plans?", true, "N") then c_persist.plans_disabled = nil crawl.mpr("skill plans reenabled") end else if crawl.yesno("disable skill plans?", true, "N") then c_persist.plans_disabled = true crawl.mpr("disabled skill plans") end end end function skill_training_needed() crawl.dump_char() if you.xl() < 9 and not c_persist.plans_disabled then return apply_skill_plan() end end local caster_gods = { ["Sif Muna"] = true, ["Vehumet"] = true, ["Kikubaaqudgha"] = true, ["Dithmenos"] = true, ["Jiyva"] = true } function choose_stat_gain() if you.xl() > 8 or c_persist.plans_disabled then return end if casters[you.class()] then if you.intelligence() < 20 then return "int" else return end end if caster_gods[you.god()] then return end local skill_name, _ = best_weapon_skill() if not skill_name then return end crawl.mpr("best skill is " .. skill_name) if skill_name:find("Ranged") or skill_name:find("Blade") then if you.dexterity() < 20 then return "dex" end elseif you.strength() < 20 then return "str" end end function w_training_target() local max_name, max_val = best_weapon_skill() if not max_name then return end local target = math.floor(max_val) + 1 if max_name == "Long Blades" or max_name == "Ranged Weapons" or max_name == "Staves" then target = 14 elseif max_name == "Short Blades" then target = 6 elseif max_name == "Unarmed Combat" then target = 6 end you.train_skill(max_name, 2) you.set_training_target(max_name, target) return max_name end function reset_c_persist() c_persist = { seen = { orbs = {} } } end if not c_persist then reset_c_persist() end --#todo clean this up (move into plans) if you.turns() == 0 then reset_c_persist() for name, _ in pairs(all_skills) do you.train_skill(name, 0) end apply_skill_plan() local c = you.class() local weapon_skill = w_training_target() if weapon_skill and (weapon_skill:find("Ranged") or weapon_skill:find("Blade")) then train("Dodging", 4) end if c == "Fighter" then train("Shields", 4) end local num_spells = casters[c] or magic_users[c] if num_spells then crawl.sendkeys("M!!!" .. string.rep("a", num_spells), 27) end end local alphabet = "bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" function first_free_slot() local occupied = {} for _, item in ipairs(items.inventory()) do local found_letter = items.index_to_letter(item.slot) occupied[found_letter] = true end for c in alphabet:gmatch(".") do if not occupied[c] then return c end end end function smart_pickup(primary) local target_letter = primary and "a" or "b" local item = you.floor_items()[1] if not item then return end rclog("item class is " .. tostring(item.class())) if item.class() == "Hand Weapons" then if items.inslot(items.letter_to_index(target_letter)) then crawl.sendkeys("=i" .. target_letter .. first_free_slot() .. "g") else crawl.sendkeys("g") end else crawl.sendkeys("e,", 13) end end function primary_pickup() return smart_pickup(true) end local aemark, armark, rejectmark = "autoequip", "autoremove", "rejected" function add_mark(it, mark) if not it.inscription:find(mark) then it.inscribe(mark) end end function remove_mark(it, mark) it.inscribe(it.inscription:gsub(mark, ""), false) end local iffy_egos = { guile = true, mayhem = true, energy = true, ["spirit shield"] = true, harm = true, death = true } ---@param it Item local function item_is_dangerous(it) if it.name_coloured():sub(1, 5) == "" then return true end if not it.is_identified then return false end if it.artefact then for prop, value in pairs(it.artprops) do local first = prop:sub(1, 1) if prop == "Bane" or prop == "Harm" or first == "*" or first == "-" or first == "^" then return true end if type(value) == "number" and value < 0 then return true end end end local class = it.class(true) local ego = it.ego() local subtype = it.subtype() if class == "armour" and iffy_egos[it.ego()] then return true end if subtype == "amulet of faith" then return true end if casters[you.class()] and (ego == "spirit shield" or subtype == "amulet of guardian spirit") then return true end return false end function autoequip(it, old_it) if old_it then add_mark(old_it, armark) end add_mark(it, aemark) rclog("marked, new inscription: ", it.inscription) c_persist.equip_pending = true return true end local function better_plus(old, new) -- for safety, avoids error when comparing orb to shield if not old.plus or not new.plus then return false end return new.plus > old.plus end local function check_plus(it) if not it then return nil end return (it.plus and it.plus > 0) or it.artefact or it.ego() end -- add heuristic for attunement if you're a caster, idk what else ---@param old Item ---@param new Item ---@param noequip? boolean ---@return boolean? local function compare_armor(old, new, noequip) if old.artefact and not new.artefact then return end -- artefacts are usually better if old.inscription:find("=i") then return end if new.equip_type ~= "Helmet" and new.ego() == "Ponderousness" then return end -- i dislike ponderousness, but the fixedart hat is ok if not old.ego() then rclog("no ego found on old item") if new.ego() or better_plus(old, new) then return noequip and true or autoequip(new, old) -- we can find more scrolls, so brands are valued higher end else if new.ego() ~= old.ego() then return -- judgement call else if better_plus(old, new) then return noequip and true or autoequip(new, old) end end end end -- add heuristic for attunement if you're a caster, idk what else ---@param old Item ---@param new Item ---@return boolean? local function compare_weapon(old, new) if old.artefact and not new.artefact then return end -- artefacts are usually better if old.inscription:find("!imp") then return end if not old.ego() then rclog("no ego found on old item") if new.ego() then return autoequip(new, old) -- we can find more scrolls, so brands are valued higher end else if new.ego() ~= old.ego() then return -- judgement call else if better_plus(old, new) then return autoequip(new, old) end end end end local function find_in_inventory(it, key) for _, cur in ipairs(items.inventory()) do if key(cur, it) then return cur end end end local function subtype_in_inventory(it, subtype) local subtype = subtype or it.subtype(false) return find_in_inventory(it, function(cur, _) return cur.subtype(false) == subtype end) end local function add_seen_subtype(it, subtype) local subtype = subtype or it.subtype(false) local it_class = it.class if not it.is_identified then rclog("add_seen_subtype error, item unidentified") end local seen_class = c_persist.seen[it_class] if seen_class then seen_class[subtype] = true else c_persist.seen[it_class] = { [subtype] = true } end end local function is_seen_subtype(it, subtype) local subtype = subtype or it.subtype(false) local class = it.class() if iffy_egos[subtype] then class = "orbs" end local seen_class = c_persist.seen[class] if not seen_class then c_persist.seen[class] = {} seen_class = c_persist.seen[class] end if seen_class[subtype] then return true else local in_inventory if class == "orbs" then end if subtype_in_inventory(it, subtype) then seen_class[subtype] = true return true else return false end end end local autopickup_handlers = {} ---@param it Item function custom_autopickup(it, name) -- if c_persist.autoequip_ongoing then -- rclog("autoequip_ongoing!") -- return false -- end if it.is_redundant then return false end -- very odd that this doesn't work by deafult local c = items.equipped_at(it.equip_type) if c then if c.inscription:find("<3") then return false end -- for items you think are best-in-slot end if it.inscription:find(armark) then remove_mark(it, armark) add_mark(it, "dropped") end if it.inscription:find(rejectmark) then return false end if it.inscription:find(aemark) then return true end local handler = autopickup_handlers[it.class()] if handler then return handler(it, name) end end clear_autopickup_funcs() -- is this neccessary? idk add_autopickup_func(custom_autopickup) local good_light_armours = { ["steam dragon scales"] = true, ["swamp dragon scales"] = true, ["acid dragon scales"] = true, ["quicksilver dragon scales"] = true, ["troll leather armour"] = true } autopickup_handlers["Armour"] = function(it, name) if it.is_useless then return end local equip_type = it.equip_type -- we may be carrying a shield for later but not wearing it. but if we aren't carrying a shield, we should be if equip_type == "offhand" then if it.encumbrance == 0 and it.is_identified then local ego = it.ego() local seen = c_persist.seen.orbs p_seen = {} for k,v in pairs(seen) do p_seen[#p_seen+1] = tostring(k) .. " " .. tostring(v) end rclog("turn", you.turns(), "inscrip", it.inscription, "seen {", table.concat(p_seen) ,"}") if not seen[ego] or it.artefact then seen[ego] = true if items.slot_is_available(equip_type) then rclog("autoequipping orb: ", it.name()) return autoequip(it) else return true end else rclog("found plain orb of known ego", it.ego()) return false end end if c_persist.branded_shield then return -- judgement call else local c = items.equipped_at("offhand") if c and c.encumbrance == 0 then -- item:subtype() c = nil end if not c then for _, i in ipairs(items.inventory()) do if i.equip_type == "offhand" and i.encumbrance > 0 then -- #todo we stop looking after the first match. will break if we don't drop the old shield since that will be used to compare c = i break end end if not c then return true end else if c.branded or c.artefact then c_persist.branded_shield = true end if you.strength() > it.encumbrance + 4 then rclog("autoequipping, ", it.name(), "to replace", c.name()) return compare_armor(c, it, not c.equipped) end end return -- this could be return false but eh end end local worn = items.equipped_at(equip_type) if worn and worn.artefact then return end -- for artefacts we evaluate manually -- rclog("found worn item " .. worn.name() .. "of equip_type " .. equip_type) if equip_type == "body armour" then if not worn then return end -- when we're naked it means we're changing and don't want to check for new stuff anyway if math.abs(it.encumbrance - worn.encumbrance) > 8 then return -- judgement call end local worn_subtype, it_subtype = worn.subtype(false), it.subtype(false) if worn.encumbrance < 8 then if it.encumbrance < 8 and not worn.ego() and (worn.plus or 0) < 2 and not good_light_armours[worn_subtype] and good_light_armours[it_subtype] then --#todo deduce our regen rate from equipped items and figure out if it's worth equipping troll leather over other light armour, since it's usually better rclog("spotted better light armour") if you.strength() > it.encumbrance + 3 then rclog("autoequipping light armor") return autoequip(it) else return true end elseif it.encumbrance < worn.encumbrance + 4 then return compare_armor(worn, it) end else return false end end if not worn then rclog("no armor in slot " .. equip_type .. ", autoequipping") return autoequip(it) elseif it.is_identified then rclog("comparing armour enchantment") return compare_armor(worn, it) end end local generic_talismans = { ["medusa talisman"] = true, ["maw talisman"] = true, ["blade talisman"] = true, ["hive talisman"] = true, ["talisman of death"] = true, ["fortress talisman"] = true } low_level_talismans = { ["scarab talisman"] = true, ["rimehorn talisman"] = true, ["quill talisman"] = true } -- https://github.com/crawl/crawl/blob/0.34.1/crawl-ref/source/item-prop.cc#L3163 local function guess_talisman_type(n) if n:find("talisman of death") or n:find("death talisman") then return ("talisman of death") end local a = n:match("[%a%-]- talisman") rclog("guess result:", a) return (a) end autopickup_handlers["Talismans"] = function(it) -- we can't use subtype because it's not available until we walk over and "id" the item, which is dumb for plain talismans but whatever -- local subtype = it.subtype() local subtype = guess_talisman_type(it.name()) if not is_seen_subtype(it, subtype) or it.artefact then rclog("found unknown talisman of subtype ", subtype) if subtype == "quill talisman" then -- no brainer to equip local ench_gloves, ench_cloak = check_plus(items.equipped_at("gloves")), check_plus(items.equipped_at("cloak")) if ench_gloves or ench_cloak then return false else return autoequip(it) end end if subtype == "inkwell talisman" and you.skill("spellcasting") > you.skill("fighting") and you.xl() < 5 then return true end if you.skill("Unarmed Combat") > 4 and best_weapon_skill() == "Unarmed Combat" then if you.xl() > 14 and low_level_talismans[subtype] then --#todo make a table categorizing them by min shapeshifting skill return false else return true end elseif generic_talismans[subtype] then return true end end rclog("talisman is a plain one of known subtype (or artifact of uninteresting subtype), ignoring") return false end autopickup_handlers["Baubles"] = function(it) if you.skill("Shapeshifting") > 1 and you.xl() < 10 then return true end end autopickup_handlers["Jewellery"] = function(it) if items.slot_is_available(it.equip_type) and not it.is_useless then return autoequip(it) end if not it.is_identified then return true end end autopickup_handlers["Potions"] = function(it) if it.name():find("moonshine") then return false end end autopickup_handlers["Scrolls"] = function(it) if it.name():find("noise") then return false -- you can just shout end end autopickup_handlers["Magical Staves"] = function(it, name) if not it.is_identified then return end if not it.artefact and is_seen_subtype(it) then return false end end local max_slots = { ["ring"] = 11, ["amulet"] = 2, ["helmet"] = 2, ["weapon"] = 2, ["offhand"] = 1, ["boots"] = 1, ["gloves"] = 1 } ---@param slot string local function get_all_equipped(slot) local num_for_slot = max_slots[slot] if not num_for_slot then error("invalid slot") end local out = {} for i = 1, num_for_slot do local it = items.equipped_at(slot, i) if it then out[#out + 1] = it end end end local melded_slots = { ["quill talisman"] = { helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["inkwell talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["rimehorn talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["spider talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["wellspring talisman"] = { ["body armour"] = true }, ["scarab talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["medusa talisman"] = { helmet = true, cloak = true }, ["spore talisman"] = { offhand = true, boots = true }, ["maw talisman"] = {}, ["serpent talisman"] = { offhand = true, ["body armour"] = true, gloves = true, boots = true, barding = true, cloak = true }, ["eel talisman"] = { offhand = true, gloves = true }, ["blade talisman"] = {}, ["lupine talisman"] = { gloves = true, boots = true, barding = true }, ["fortress talisman"] = { offhand = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["granite talisman"] = { ["body armour"] = true, gloves = true, boots = true, barding = true }, ["hive talisman"] = { ["body armour"] = true, helmet = true }, ["dragon-coil talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, ["riddle talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true }, ["sanguine talisman"] = {}, ["talisman of death"] = {}, ["storm talisman"] = { offhand = true, ["body armour"] = true, helmet = true, gloves = true, boots = true, barding = true, cloak = true }, }; function equip_marked() if you.turn_is_over() then rclog("turn is over, skipping equip") return end ---@type table local to_equip = {} local cur_equipped = {} local to_drop = {} local equip_command = "" local equipped_talisman, pending_talisman for _, it in ipairs(items.inventory()) do if it.equipped then if it.inscription:find(aemark) then remove_mark(it, aemark) c_persist.autoequip_pending = nil -- it should get reenabled next turn if there are other marked items in LOS end if it.class() == "Talismans" then equipped_talisman = it else -- we could get non-talisman gear by probing (guessing at max slots) item.equipped_at, but since we're looping inv for equipped talisman might as well get the info here ---@type string local it_equip_type = it.equip_type if not cur_equipped[it_equip_type] then cur_equipped[it_equip_type] = {} end table.insert(cur_equipped[it_equip_type], it) end end if it.inscription:find(armark) or it.inscription:find(rejectmark) then table.insert(to_drop, it) end if it.inscription:find(aemark) then if it.class() == "Talismans" then pending_talisman = it else if not it.equip_type then error("item " .. it.name() .. " has no equip type!") end if not to_equip[it.equip_type] then rclog("queueing: " .. it.name() .. "to equip") local equip_type = it.equip_type if not equip_type then error("no equip_type for marked item " .. it.name()) end to_equip[equip_type] = it else rclog("multiple items pending to the same slot:") rclog(table.concat({ to_equip[it.equip_type].name(), it.name() })) end end end end if equipped_talisman then rclog(equipped_talisman.name(), " is equipped, looking for pending armour") local subtype = equipped_talisman.subtype() if not subtype then crawl.mpr(equipped_talisman.name(), "has no subtype!") crawl.more() subtype = guess_talisman_type(equipped_talisman.name()) end for equip_type, it in pairs(to_equip) do if it.class() == "Armour" and melded_slots[subtype][equip_type] then if you.transform() == "flux" then if crawl.yesno("exit flux form to equip " .. it.name() .. "?") then sendkeys("af") return else remove_mark(it, aemark) to_equip[equip_type] = nil end end crawl.dump_char() local t_remove_command = "e" local slot = items.index_to_letter(equipped_talisman.slot) if crawl.yesno("keep " .. equipped_talisman.name() .. " when equipping " .. it.name() .. "?", true, "Y") then crawl.mpr("unequipping talisman before autoequip") c_persist.old_talisman = slot else t_remove_command = "d" end sendkeys(t_remove_command, slot, 13) return end end end rclog("forming equip command!") for equip_type, it in pairs(to_equip) do rclog("equipping", it.name()) -- we could try to add all pending items in the same slot to a command, but that requires evaluating available slots which is too much of a hassle if not it then error(" ??no item pending for slot " .. equip_type) end if it.is_useless or item_is_dangerous(it) then local equipped_names = {} for _, i in ipairs(cur_equipped[equip_type] or {}) do equipped_names[#equipped_names + 1] = i.name() end equipped_names = next(equipped_names) and equipped_names or { "none" } --#todo replace this pair of yesnos with a more sophisticated prompt like in the plan. would also let you quickly drop the replaced item if crawl.yesno("equip " .. it.name() .. "? current equipped is\n" .. table.concat(equipped_names, "\n")) then equip_command = equip_command .. "e" .. items.index_to_letter(it.slot) else if not crawl.yesno("keep it?", true) then to_drop[#to_drop + 1] = it add_mark(it, rejectmark) end remove_mark(it, aemark) to_equip[equip_type] = nil end else equip_command = equip_command .. "e" .. items.index_to_letter(it.slot) end end if next(to_equip) then c_persist.autoequip_ongoing = true local drop_command = "d" for _, it in ipairs(to_drop) do if it.is_melded then error("item " .. it.name() .. " is melded during equip step!") end crawl.mpr("preparing to drop " .. it.name()) drop_command = drop_command .. items.index_to_letter(it.slot) end if drop_command:len() > 1 then sendkeys(drop_command, 13) -- maybe it's more optimal to remove then equip then drop but meh return end for slot, it in pairs(to_equip) do if not items.slot_is_available(slot) then crawl.mpr("want to equip " .. it.name() .. " but no slot available") crawl.more() remove_mark(it, aemark) end end sendkeys(equip_command) return else rclog("nothing to equip!") end rclog("done with gear equip, checking talisman") if pending_talisman then rclog("found pending talisman:") sendkeys("e" .. items.index_to_letter(pending_talisman.slot)) elseif c_persist.old_talisman then local t = get_item(c_persist.old_talisman) if not t or t.equipped then rclog("stored talisman is missing, clearing c_persist.old_talisman") c_persist.old_talisman = nil else rclog("found saved talisman ", t.name()) sendkeys("e" .. c_persist.old_talisman) return end end c_persist.equip_pending = nil c_persist.autoequip_ongoing = nil rclog("equip_marked done!") end function ready() if you.feel_safe() and c_persist.equip_pending then rclog("equip triggered") equip_marked() end end } #end lua ##### Ancient versions ############################################## # If you're used to the interface of ancient versions of Crawl, you may # get back parts of it by uncommenting the following options: # include = 034_command_keys.txt # target_unshifted_dirs = true # clear_messages = true # # And to revert monster glyph and colouring changes: # include = 034_monster_glyphs.txt # include = 052_monster_glyphs.txt # include = 060_monster_glyphs.txt # include = 071_monster_glyphs.txt # include = 080_monster_glyphs.txt # include = 0.9_monster_glyphs.txt ##### 1- Starting Screen ############################################ # # name = Delilah # remember_name = false # weapon = (short sword | falchion | quarterstaff | hand axe | spear | mace # | trident | unarmed | random) # species = (Human |...| Vampire | random) # job = (Fighter |...| Wanderer | random) # random_pick = true # good_random = false restart_after_game = true ##### 2- File System ############################################### # # crawl_dir = # morgue_dir = morgue # save_dir = saves # macro_dir = settings/ # sound = : ##### 3- Interface ################################################# # ##### 3-a Picking up and Dropping ############### # # Rods are \ here. autopickup = $}|:!/?♦= autopickup_exceptions += curare explore_stop_pickup_ignore += scroll explore_stop_pickup_ignore += potion auto_exclude += oklob,statue,curse skull,roxanne,hyperactive,lightning spire,sigmund,grinder # auto_exclude += mimic # tc_reachable = blue # tc_dangerous = cyan # tc_disconnected = darkgrey # tc_excluded = lightmagenta # tc_exclude_circle = red # Interrupting run and rest compound commands: force_more_message += 3 scrolls labelled force_more_message += - a scroll of identify force_more_message += you flicker for a moment runrest_ignore_message ^= You feel.*sick runrest_ignore_message ^= sluggish runrest_ignore_message ^= creaks loudly runrest_ignore_monster ^= butterfly:1 runrest_ignore_message ^= emerges from the mists of memory! runrest_stop_message += You hear a.* slurping noise # runrest_ignore_message += Jiyva's power touches on your attributes runrest_stop_message ^= god:wrath finds you runrest_stop_message ^= less wooden runrest_stop_message ^= 4 scrolls labelled runrest_stop_message ^= able to move again runrest_ignore_message += duration:.* runrest_ignore_message += recovery:.* runrest_ignore_message += autoequip # A good number of runrest_messages are defined by default; you may want to # clear them or override some. # trapwalk_safe_hp = dart:20,needle:15,arrow:35,bolt:45,spear:40,blade:95 #rest_wait_both = true ##### 3-h Command Enhancements ################## # # auto_list = false # auto_switch = true # easy_open = false # easy_unequip = false equip_unequip = true easy_confirm = safe allow_self_target = no # confirm_butcher = (auto | always | never) # prefer_safe_chunks = false # easy_eat_chunks = true # easy_eat_gourmand = true # easy_eat_contaminated = true auto_eat_chunks = true auto_drop_chunks = rotten # prompt_for_swap = false # easy_quit_item_prompts = false # easy_exit_menu = true # sort_menus = pickup: true : art, ego, basename, qualname, curse, qty # sort_menus = inv: true : equipped, freshness, charged autofight_throw = false autofight_caught = true spell_slot ^= shock:z spell_slot ^= tongue:z spell_slot ^= discharge:d spell_slot ^= airstrike:q spell_slot ^= swiftness:s spell_slot ^= spire:p spell_slot ^= arcjolt:d spell_slot ^= destruction:o spell_slot ^= apportation:y spell_slot ^= blink:b spell_slot ^= magic dart:z spell_slot ^= mystic:x spell_slot ^= stone arrow:x spell_slot ^= lee:a spell_slot ^= stoneskin:s spell_slot ^= poison arrow:p spell_slot ^= haste:h spell_slot ^= battlesphere:s spell_slot ^= mephitic:m spell_slot ^= spray:c spell_slot ^= conjure flame:f spell_slot ^= fireball:q spell_slot ^= sticky flame:e spell_slot ^= foxfire:z spell_slot ^= vampiric draining:d spell_slot ^= pain:z spell_slot ^= skeleton:s spell_slot ^= animated dead:a spell_slot ^= control undead:c spell_slot ^= appendage:z spell_slot ^= sticks:s spell_slot ^= spider form:x spell_slot ^= ice form:c spell_slot ^= blade hands:a spell_slot ^= sting:x spell_slot ^= throw:x spell_slot ^= freezing:c spell_slot ^= vhi's:v spell_slot ^= fugue:f spell_slot ^= putrefaction:u spell_slot ^= haunt:hand spell_slot ^= death channel:ca spell_slot ^= infest:i spell_slot ^= call imp:i spell_slot ^= canine:d spell_slot ^= seismosaur:e spell_slot ^= crocodile:r spell_slot ^= forest:f spell_slot ^= hydra:hy spell_slot ^= knell:km spell_slot ^= viper:pv ability_slot ^= recall:r ability_slot ^= smit:s ability_slot ^= lash:l ability_slot ^= heavenly storm:r ability_slot ^= invis:i ability_slot ^= blink:b ability_slot ^= torchlight:t message_colour += mute:unmeld message_colour += mute:meld message_colour += mute:turn into message_colour += mute:rush to embrace message_colour += mute:shell of carrion and bone # autofight_stop = 30 ##### 3-i Messages and Display Enhancements ##### # hp_warning = 70 # mp_warning = 0 # hp_colour = 50:yellow, 25:red # mp_colour = 50:yellow, 25:red # stat_colour = 1:lightred, 3:red # status_caption_colour = yellow # mlist_min_height = 4 # msg_min_height = 7 # msg_max_height = 10 # mlist_allow_alternate_layout = true # messages_at_top = true # mlist_targetting = true show_gold_turns = true # show_game_turns = false # show_newturn_mark = false # clear_messages = true # small_more = true # show_more = false show_inventory_weights = true # item_stack_summary_minimum = 5 # msg_condense_repeats = false # msg_condense_short = false # skill_focus = false show_travel_trail = true ##### 3-j Colours (messages and menus) ########## # # Food is colour coded by default as follows: # # yellow = preferred food ($preferred) # darkgrey = cannot be eaten at all ($inedible) # lightred = causes rot ($rot-inducing) # magenta = dangerous to use, mutagenic ($dangerous, $mutagenic) # red = considered "evil" by your deity ($evil) # lightgreen = poisonous ($poisonous) # brown = contaminated, may cause nausea ($contaminated) # # You can change colours by using something like # $contaminated := magenta # You can redefine inventory items like this: # menu_colour ^= inventory:magenta:shillelagh # or ones in pick-up dialogs: # menu_colour ^= pickup:green:god gift # Colouring of messages # Messages are colour coded as follows: # # lightred = really important messages ($danger) # yellow = significant messages ($warning) # lightred = item damaged/destroyed ($item_dmg [same as $danger]) # darkgrey = boring, message clutter ($boring) # # Notes also use, quite misnamed, menu_colour: # menu_colour ^= notes:white:Reached XP level # These lines will suppress extra feedback messages from travel/shift-running. # # message_colour ^= mute:warning:comes into view message_colour ^= mute:You start (searching|resting) # message_colour ^= mute:Done searching # message_colour ^= mute:(HP|Magic) restored # message_colour ^= mute:warning:Not with.*(in view|monsters around) # You can also enforce a --more-- prompt for messages ## Avoid wasting turns with aborted actions: # force_more_message += There are no visible monsters within range force_more_message += This wand has no charges force_more_message += Ouch! force_more_message += finished your manual force_more_message += Lucky! force_more_message += protects you from harm force_more_message += wielding a \+[4-9] force_more_message -= feel clever force_more_message += you encounter .* (dread lich|ereshkigal|grinder|sphinx|ogre mage|orc sorcerer|rupert|vampire knight|xtahua) ##### 3-k Firing Commands ####################### # # fire_items_start = a # fire_order = launcher, return # fire_order += javelin / dart / stone / rock / spear / net / handaxe / dagger / club # fire_order += inscribed ##### 3-l Channels ############################## # # channel.multiturn = mute ##### 3-m Inscriptions ########################## # autoinscribe += potion:!q # autoinscribe_cursed = false autoinscribe += scroll of ([^i]|im):!r autoinscribe += wand of heal wounds:!V autoinscribe += wand of teleport:!V autoinscribe += distortion:!w ai += of faith:!P # show_god_gift = yes|unident|no ##### 3-n Macro related Options ################# # # flush.failure = false # flush.command = true # flush.message = true ##### 3-o Tile related Options ################## # # tile_show_items = !?/%=([)x}:|\ # tile_skip_title = true # tile_menu_icons = false tile_web_mouse_control = true ### The following lines define the colours of various objects within the ### tiles minimap. See options_guide.txt for more details. # tile_player_col = white # tile_monster_col = red # tile_neutral_col = red # tile_peaceful_col = lightred # tile_friendly_col = lightred # tile_plant_col = darkgreen # tile_item_col = green # tile_unseen_col = black # tile_floor_col = lightgrey # tile_wall_col = darkgrey # tile_mapped_wall_col = blue # tile_door_col = brown # tile_downstairs_col = magenta # tile_upstairs_col = blue # tile_feature_col = cyan # tile_trap_col = yellow # tile_water_col = grey # tile_lava_col = grey # tile_excluded_col = darkcyan # tile_excl_centre = darkblue # tile_window_col = yellow # If Crawl's response rate is too slow, try increasing the update rate. # tile_update_rate = 1000 # If Crawl is lagging when running or resting, try increasing this number. # tile_runrest_rate = 100 # tile_key_repeat_delay = 200 # tile_tooltip_ms = 500 # tile_tag_pref = enemy ### Note: setting window, map or font sizes to '0' implies auto-sizing. tile_full_screen = false tile_window_width = 0 tile_window_height = 0 tile_use_small_layout = false tile_map_pixels = 4 # tile_cell_pixels = 32 # tile_filter_scaling = true # tile_force_overlay = true # tile_font_crt_file = VeraMono.ttf # tile_font_crt_size = 15 # tile_font_stat_file = VeraMono.ttf # tile_font_stat_size = 16 # tile_font_msg_file = VeraMono.ttf # tile_font_msg_size = 14 # tile_font_tip_file = VeraMono.ttf # tile_font_tip_size = 15 # tile_font_lbl_file = Vera.ttf # tile_font_lbl_size = 14 # tile_font_ft_light = false tile_show_minihealthbar = false tile_show_minimagicbar = false tile_layout_priority = minimap, inventory, gold_turn, monster, spell, command ##### 4- Dump File ################################################# # ##### 4-a Saving ################################ # # dump_on_save = false ##### 4-b Items and Kill List ################### # # kill_map = friend:you, other:you # dump_kill_places = (single | all | none) # dump_kill_breakdowns = true # dump_item_origins = all,artifacts,ego_arm,ego_weap,jewellery,runes # dump_item_origin_price = -1 dump_message_count = 40 # dump_order = header, hiscore, stats, misc, notes, inventory, # dump_order += turns_by_place, skills, spells, overview, mutations, # dump_order += messages, screenshot, monlist, kills_by_place, kills # dump_order += action_counts dump_book_spells = true ##### 4-c Notes ################################# # # user_note_prefix=@@@ note_hp_percent = 15 # note_skill_levels = 1,5,10,15,27 # note_all_skill_levels = true # note_skill_max = false # note_xom_effects = false # note_chat_messages = false # note_items += rod of, acquirement, preservation, running, of Zot # note_monsters += orb of fire, ancient lich, Sigmund note_messages += protects you from harm ##### 5- Miscellaneous ############################################# # ##### 5-a All OS ################################ # # mouse_input = true # wiz_mode = no # char_set = ascii # use_fake_player_cursor = false # # Translation to use (in 0.11, only badly incomplete descriptions; Polish # has god messages and a few misc, Greek has sheep). # language = pl|fr|zh|ko|fi|ru|el|da|li|pt|de|lv|cs|hu # # Joke translations (complete!). # language = dwarven|jagerkin|kraut|runes|wide # # colour.lightgray = black # colour.lightcyan = cyan # colour.yellow = brown # # show_player_species = true # See options_guide.txt for the options # cset, feature, mon_glyph, item_glyph ## Highlight the edge of unexplored terrain. # feature = explore horizon {',,green} ##### 5-b Windows console ####################### # # dos_use_background_intensity = true # background = black ##### 5-c Unix console ########################## # # use_fake_cursor = false ##### 5-d Alternative keybinding files ########## # # Alternative vi bindings for Dvorak users. # include = dvorak_command_keys.txt # # Override the vi movement keys with a non-command. # include = no_vi_command_keys.txt macros += M \{-266} ===toggle_plans macros += M " ===toggle_ring macros += M / G> macros += M O ===smart_rest macros += M o ===short_explore macros += M \{15} ===explore_no_rest macros += M , ===smart_pickup macros += M \{188} ===primary_pickup # macros += M \{-2956} ===primary_pickup # the same alt+, chord is two different codes on console and tiles, so enable the correct one bindkey = [{] CMD_LUA_CONSOLE rest_wait_both = false explore_auto_rest = true rest_wait_ancestor = true rest_wait_percent = 66 explore_stop = greedy_pickup_smart,stairs,altars,portals,branches,runed_doors,glowing_items wall_jump_move = false default_manual_training = true show_paged_inventory = false auto_hide_spells = true