########.\init.txt######## ################################# ####### Main options ############ ################################# autofight_stop = 65 hp_warning = 20 item_stack_summary_minimum = 8 default_manual_training = true messages_at_top = true show_more = false autofight_caught = true rest_wait_both = true rest_wait_ancestor = true sort_menus = true:equipped,art,ego,glowing,identified,basename,qualname,>qty drop_filter += useless_item, forbidden fire_order = silver javelin, javelin, silver boomerang, boomerang, curare-tipped dart, poisoned dart, stone ################################# ####### Explore options ######### ################################# explore_delay = -1 travel_delay = -1 rest_delay = -1 view_delay = 500 show_travel_trail = true explore_greedy_visit = artefacts,glowing_items,stacks explore_stop = artefacts,altars,branches,portals,runed_doors,greedy_pickup_smart explore_stop_pickup_ignore += scroll, potion, wand, stone, dart, boomerang, javelin ####################################### ####### Autopickup exceptions ######### ####################################### autopickup_exceptions ^= useless_item, = cutoff/2 then return "-" end if res == 2 and hp >= cutoff/3 then return "-" end if res == 3 and hp >= cutoff/5 then return "-" end else if res == 0 and hp < cutoff then return "+" end if res == 1 and hp > cutoff/2 then return "+" end if res == 2 and hp > cutoff/3 then return "+" end if res == 3 and hp > cutoff/5 then return "+" end end end -------------------------------------------- ------------------- Hook ------------------- -------------------------------------------- function ready_force_mores() local activated = {} local deactivated = {} local hp, maxhp = you.hp() local willpower = you.willpower() local res_mut = you.res_mutation() local res_pois = you.res_poison() local res_elec = you.res_shock() local res_corr = you.res_corr() local res_fire = you.res_fire() local res_cold = you.res_cold() local res_drain = you.res_draining() local int, maxint = you.intelligence() for i,v in ipairs(fm_patterns) do local msg = nil if type(v.pattern) == "table" then for j, p in ipairs(v.pattern) do if not msg then msg = p else msg = msg .. "|" .. p end end else msg = v.pattern end msg = "monster_warning:(?= v.cutoff then action = "-" elseif not active_fm[i] and you.xl() < v.cutoff then action = "+" end elseif v.cond == "maxhp" then if active_fm[i] and maxhp >= v.cutoff then action = "-" elseif not active_fm[i] and maxhp < v.cutoff then action = "+" end elseif v.cond == "hp" then if active_fm[i] and hp >= v.cutoff then action = "-" elseif not active_fm[i] and hp < v.cutoff then action = "+" end elseif v.cond == "int" then if active_fm[i] and int >= v.cutoff then action = "-" elseif not active_fm[i] and int < v.cutoff then action = "+" end elseif v.cond == "will" then if active_fm[i] and willpower >= v.cutoff then action = "-" elseif not active_fm[i] and willpower < v.cutoff then action = "+" end elseif v.cond == "mut" then if active_fm[i] and res_mut > 0 then action = "-" elseif not active_fm[i] and res_mut == 0 then action = "+" end elseif v.cond == "pois" then if active_fm[i] and (res_pois > 0 or hp >= v.cutoff) then action = "-" elseif not active_fm[i] and res_pois == 0 and hp < v.cutoff then action = "+" end elseif v.cond == "elec" then if active_fm[i] and (res_elec > 0 or hp >= v.cutoff) then action = "-" elseif not active_fm[i] and res_elec == 0 and hp < v.cutoff then action = "+" end elseif v.cond == "corr" then if active_fm[i] and (res_corr or hp >= v.cutoff) then action = "-" elseif not active_fm[i] and not res_corr and hp < v.cutoff then action = "+" end elseif v.cond == "fire" then action = get_three_pip_action(active_fm[i], hp, v.cutoff, res_fire) elseif v.cond == "cold" then action = get_three_pip_action(active_fm[i], hp, v.cutoff, res_cold) elseif v.cond == "drain" then action = get_three_pip_action(active_fm[i], hp, v.cutoff, res_drain) end if action == "+" then activated[#activated + 1] = fm_name elseif action == "-" then deactivated[#deactivated + 1] = fm_name end if action then local opt = "force_more_message " .. action .. "= " .. msg crawl.setopt(opt) active_fm[i] = not active_fm[i] end end if #activated > 0 and notify_fm then crawl.mpr("Activating force_mores: " .. table.concat(activated, ", "), "plain") end if #deactivated > 0 and notify_fm then crawl.mpr("Deactivating force_mores: " .. table.concat(deactivated, ", "), "plain") end end } ########dynamic-options.rc######## { ---------------------- ---- race-specific ---- ---------------------- if you.race() == "Demonspawn" then crawl.setopt("more += monster_warning:wielding.*of holy wrath") elseif you.race() == "Formicid" then crawl.setopt("more -= monster_warning:curare") elseif you.race() == "Gargoyle" then crawl.setopt("more -= monster_warning:curare") elseif you.race() == "Ghoul" then crawl.setopt("more -= monster_warning:curare") crawl.setopt("more += monster_warning:wielding.*of holy wrath") elseif you.race() == "Gnoll" then crawl.setopt("message_colour ^= mute:intrinsic_gain:skill increases to level") elseif you.race() == "Mummy" then crawl.setopt("more -= monster_warning:curare") crawl.setopt("more += monster_warning:wielding.*of holy wrath") end ---------------------- ---- god-specific ---- ---------------------- local cur_god = "No God" local function set_god_options() if you.god() ~= cur_god then cur_god = you.god() if cur_god == "Beogh" then crawl.setopt("autopickup_exceptions ^= >scrolls? of immolation") crawl.setopt("runrest_ignore_message += no longer looks unusually strong") crawl.setopt("force_more_message += Your orc.*dies") crawl.setopt("macros += M 1 tf") crawl.setopt("macros += M 2 ta") crawl.setopt("macros += M 3 tr") crawl.setopt("macros += M 4 tg") crawl.setopt("macros += M 6 ab") elseif cur_god == "Dithmenos" then crawl.setopt("force_more_message += god:You are shrouded in an aura of darkness") crawl.setopt("force_more_message += god:You now sometimes bleed smoke") crawl.setopt("force_more_message += god:You.*no longer.*bleed smoke") crawl.setopt("force_more_message += god:Your shadow no longer tangibly mimics your actions") crawl.setopt("force_more_message += god:Your shadow now sometimes tangibly mimics your actions") elseif cur_god == "Fedhas" then crawl.setopt("force_more_message += god:Fedhas invokes the elements against you") elseif cur_god == "Hepliaklqana" then crawl.setopt("runrest_ignore_message ^= emerges from the mists of memory") elseif cur_god == "Jiyva" then crawl.setopt("force_more_message += god:will now unseal the treasures of the Slime Pits") crawl.setopt("force_more_message += god:Jiyva alters your body") crawl.setopt("force_more_message += god:splits in two") crawl.setopt("force_more_message += god:Your prayer is over.") elseif cur_god == "Kikubaaqudgha" then crawl.setopt("force_more_message += god:Kikubaaqudgha will grant you") elseif cur_god == "Lugonu" then crawl.setopt("force_more_message += god:Lugonu will now corrupt your weapon") crawl.setopt("force_more_message += god:Lugonu sends minions to punish you") elseif cur_god == "Okawaru" then crawl.setopt("force_more_message += god:Okawaru sends forces against you") elseif cur_god == "Qazlal" then crawl.setopt("force_more_message += god:resistances upon receiving elemental damage") crawl.setopt("force_more_message += god:You are surrounded by a storm which can block enemy attacks") elseif cur_god == "The Shining One" then crawl.setopt("force_more_message += god:Your divine shield starts to fade.") crawl.setopt("force_more_message += god:Your divine shield fades away.") elseif cur_god == "Trog" then crawl.setopt("force_more_message += god:You feel the effects of Trog's Hand fading") crawl.setopt("force_more_message += god:You feel less resistant to hostile enchantments") elseif cur_god == "Uskayaw" then crawl.setopt("force_more_message += god:You can no longer pass through a line of other dancers.") elseif cur_god == "Wu Jian Council" then crawl.setopt("runrest_ignore_message += heavenly storm settles") elseif cur_god == "Xom" then crawl.setopt("force_more_message += god:") crawl.setopt("force_more_message += staircase.*moves") crawl.setopt("force_more_message += Some monsters swap places") elseif cur_god == "Yredelemnul" then crawl.setopt("force_more_message += god:soul is now ripe for the taking") crawl.setopt("force_more_message += god:soul is no longer ripe for the taking") crawl.setopt("force_more_message += god:dark mirror aura disappears") elseif cur_god == "Zin" then crawl.setopt("force_more_message += god:will now cure all your mutations") end end end --------------------- ---- xl-specific ---- --------------------- local warn_early_levels = false local warn_mid_levels = false local function set_xl_options() if not warn_early_levels and you.xl() <= 5 then warn_early_levels = true crawl.setopt("force_more_message += wielding.*(vorpal|(? 5 then warn_early_levels = false crawl.setopt("force_more_message -= wielding.*(vorpal|(? 10 then warn_mid_levels = false crawl.setopt("force_more_message -= wielding.*of electrocution") end end ------------------------ ---- skill-specific ---- ------------------------ local ignoring_spellcasting = false local function set_skill_options() -- Ignore spellcaster items if you have no spellcasting skill, and wearing some armour without much armour skill local arm = items.equipped_at("body armour") if not ignoring_spellcasting and you.skill("Spellcasting") == 0 and arm and arm.encumbrance > you.skill("Armour") then ignoring_spellcasting = true crawl.setopt("explore_stop_pickup_ignore += spellbooks") crawl.setopt("autopickup_exceptions ^= >scrolls? of amnesia, >potions? of brilliance, >ring of wizardry") crawl.setopt("runrest_ignore_message += You add the spell") elseif ignoring_spellcasting and not (you.skill("Spellcasting") == 0 and arm and arm.encumbrance > you.skill("Armour")) then ignoring_spellcasting = false crawl.setopt("explore_stop_pickup_ignore -= spellbooks") crawl.setopt("autopickup_exceptions -= >scrolls? of amnesia, >potions? of brilliance, >ring of wizardry") crawl.setopt("runrest_ignore_message -= You add the spell") end end ------------------------------------------ ------------------ Hook ------------------ ------------------------------------------ function ready_dynamic_options() set_god_options() set_xl_options() set_skill_options() end } ########startup.rc######## { ------------------------------------------------- ---- Start with travel speed to slowest ally ---- ------------------------------------------------- -- Make sure this comes before Skill menu on startup, since they both use crawl.sendkeys() local need_toggle_travel_speed = true if you.turns() == 0 and need_toggle_travel_speed then need_toggle_travel_speed = false crawl.sendkeys(20) end --------------------------------------------- ---- Skill menu on startup (by rwbarton) ---- --------------------------------------------- local need_skills_opened = true if you.turns() == 0 and need_skills_opened then need_skills_opened = false crawl.sendkeys("m!!") end } ### Pickup and alert ### ########pickup-alert/pa-util.rc######## { function show_alert_msg(alert_text, item_name) crawl.mpr("----"..alert_text..""..item_name.."----") end function insert_item_and_less_enchanted(table_name, item_name) -- Add an item name to a table, along with all less enchanted versions -- e.g. "+3 flail" will add: "+3 flail", "+2 flail", "+1 flail", "+0 flail" if util.contains(table_name, item_name) then return end table.insert(table_name, item_name) -- Add less enchanted items too local plus = tonumber(item_name:sub(2,2)) if not plus then return end if plus > 0 then if tonumber(item_name:sub(3,3)) then plus = 10 * plus + tonumber(item_name:sub(3,3)) end for i=plus,1,-1 do item_name = item_name:gsub("+"..i, "+"..(i-1)) table.insert(table_name, item_name) end end end function has_ego(it) if it.class(true) == "weapon" then return it.branded end if it.artefact then return true end if it.branded then return true end local qualname = it.name("qual") if qualname:find("troll leather") then return true end if qualname:find("dragon scales") then return true end return false end function get_size_factor() local race = you.race() if race == "Spriggan" then return 4 elseif race == "Kobold" then return 2 elseif race == "Formicid" or race == "Armataur" or race == "Naga" or race == "Ogre" or race == "Troll" then return -2 else return 0 end end function get_shield_penalty(sh) return 2/5 * sh.encumbrance * sh.encumbrance / (20 - 3 * get_size_factor()) * (27 - you.skill("Shields")) / 27 end local summon_gods = { "Beogh", "Jiyva", "Yredelemnul" } function you_have_summons() return you.skill("Summonings") + you.skill("Necromancy") > 0 or util.contains(summon_gods, you.god()) end -------------------------------------------------- --------------------- Armour --------------------- -------------------------------------------------- function get_armour_ac(it) local it_plus = it.plus if not it_plus then it_plus = 0 end if it.artefact and it.fully_identified then local art_ac = it.artprops["AC"] if art_ac then it_plus = it_plus + art_ac end end if it.artefact and it.fully_identified then local art_ac = it.artprops["AC"] if art_ac then it_plus = it_plus + art_ac end end local it_ac = it.ac if you.get_base_mutation_level("deformed body", true, false, false) > 0 then it_ac = it_ac / 2 end return it_ac * (you.skill("Armour") / 22 + 1) + it_plus end function get_shield_sh(it) local dex = you.dexterity() if it.artefact and it.fully_identified then local art_dex = it.artprops["Dex"] if art_dex then dex = dex + art_dex end end local cur = items.equipped_at("shield") if cur and cur.artefact and cur.slot ~= it.slot then local art_dex = cur.artprops["Dex"] if art_dex then dex = dex - art_dex end end local it_plus = it.plus if not it_plus then it_plus = 0 end local it_ac = it.ac if you.get_base_mutation_level("deformed body", true, false, false) > 0 then it_ac = it_ac / 2 end local block = it_ac local race = you.race() if race == "Spriggan" then if it.name("base"):find("buckler") then block = block + 2 end elseif race == "Kobold" then local basename = it.name("base") if basename:find("buckler") then block = block + 1 elseif basename:find("kite shield") then block = block + 0.5 end elseif race == "Ogre" or race == "Troll" then if it.name("base"):find("kite shield") then block = block - 0.5 end end local stat_bonus = dex * 38 * (it_ac + 13) / 5200 local skill = you.skill("Shields") local addend if skill >= 3 then addend = .38*skill else addend = .57 + 0.19*skill end return block * (1+skill/40) + it_plus + stat_bonus + addend end function get_armour_ev(it) -- This function computes the armour-based component to standard EV (not paralysed, etc) -- Factors in stat changes from this armour and removing current one local str = you.strength() local dex = you.dexterity() local art_ev = 0 -- Adjust str/dex/EV for artefact stat changes local worn = items.equipped_at("body armour") if worn and worn.artefact then if worn.artprops["Str"] then str = str - worn.artprops["Str"] end if worn.artprops["Dex"] then dex = dex - worn.artprops["Dex"] end if worn.artprops["EV"] then art_ev = art_ev - worn.artprops["EV"] end end local no_art_dex = dex if it.artefact then if it.artprops["Str"] then str = str + it.artprops["Str"] end if it.artprops["Dex"] then dex = dex + it.artprops["Dex"] end if it.artprops["EV"] then art_ev = art_ev + it.artprops["EV"] end end local size_factor = get_size_factor() local dodge_bonus = (8 + you.skill("Dodging") * dex) / (20 - size_factor) local normalize_zero_to_zero = (8 + you.skill("Dodging") * no_art_dex) / (20 - size_factor) armor_penalty = it.encumbrance - 3 local modifier = 0 if armor_penalty <= 0 then modifier = 1 elseif armor_penalty > 0 then if armor_penalty >= str then modifier = str / (armor_penalty * 2) else modifier = 1 - armor_penalty / (str * 2) end end local final_dodge_bonus = dodge_bonus * modifier local aevp = 2 * it.encumbrance * it.encumbrance * (45 - you.skill("Armour")) / (5 * (str + 3) * 45) return final_dodge_bonus - aevp + art_ev - normalize_zero_to_zero end --------------------------------------------------- --------------------- Weapons --------------------- --------------------------------------------------- function get_hands(it) if you.race() ~= "Formicid" then return it.hands end st, _ = it.subtype() if st == "giant club" or st == "giant spiked club" then return 2 end return 1 end function get_weap_min_delay(it) -- This is an abbreviated version of the actual calculation. -- Intended only to be used to prevent skill from reducing too far in get_weap_delay() local basename = it.name("base") local st, _ = it.subtype() local base_delay = it.delay local min_delay = base_delay / 2 if it.weap_skill == "Short Blades" and min_delay > 5 then min_delay = 5 end if min_delay > 7 then min_delay = 7 end if basename:find("longbow") then min_delay = 6 elseif (basename:find("crossbow") or basename:find("arbalest")) and min_delay < 10 then min_delay = 10 end return min_delay end function get_weap_delay(it) local delay = it.delay - you.skill(it.weap_skill)/2 local min_delay = get_weap_min_delay(it) if delay < min_delay then delay = min_delay end if it.ego() == "Speed" then delay = delay * 2 / 3 end if delay < 3 then delay = 3 end local sh = items.equipped_at("shield") if sh then delay = delay + get_shield_penalty(sh) end return delay / 10 end function get_slay_bonuses() local sum = 0 -- Slots can go as high as 18 afaik for i = 0,20 do it = items.equipped_at(i) if it then if it.name("base") == "ring" then if it.artefact then local name = it.name() local idx = name:find("Slay+") if idx then local slay = tonumber(name:sub(idx+5, idx+5)) if slay == 1 then local next_digit = tonumber(name:sub(idx+6, idx+6)) if next_digit then slay = 10 + next_digit end end sum = sum + slay end elseif it.ego(true) == "Slay" then sum = sum + it.plus end elseif it.artefact and (it.class(true) == "armour" or it.name("base") == amulet) then local slay = it.artprops["Slay"] if slay then sum = sum + slay end end end end if you.race() == "Demonspawn" then sum = sum + 3 * you.get_base_mutation_level("augmentation", true, false, true) sum = sum + you.get_base_mutation_level("sharp scales", true, false, true) end return sum end function get_staff_bonus_dmg(it, no_brand_dmg) if no_brand_dmg and basename ~= "staff of earth" and basename ~= "staff of conjuration" then return 0 end local evo_skill = you.skill("Evocations") local basename = it.name("base") local school if basename == "staff of fire" then school = "Fire Magic" elseif basename == "staff of cold" then school = "Ice Magic" elseif basename == "staff of earth" then school = "Earth Magic" elseif basename == "staff of air" then school = "Air Magic" elseif basename == "staff of poison" then school = "Poison Magic" elseif basename == "staff of death" then school = "Necromancy" elseif basename == "staff of conjuration" then school = "Conjurations" else crawl.mpr("ERROR:UNRECOGNIZED STAFF:"..it.name("base"), error) return 0 end local spell_skill = you.skill(school) local chance = (evo_skill + spell_skill/2) / 15 if chance > 1 then chance = 1 end -- 0.625 is an acceptable approximation -- Earth magic does more, but reduced by armour. Poison/draining bonus effects are ignored. local avg_dmg = 0.625 * (evo_skill/2 + spell_skill) return avg_dmg*chance end function get_weap_dps(it, no_brand_dmg) -- Returns an adjusted weapon damage = damage * speed -- Includes stat/slay changes between weapon and the one currently wielded -- Aux attacks not included local it_plus = it.plus if not it_plus then it_plus = 0 end -- Adjust str/dex/slay from artefacts local str = you.strength() local dex = you.dexterity() -- Adjust str/dex/EV for artefact stat changes if not it.equipped then local wielded = items.equipped_at("weapon") if wielded and wielded.artefact then if wielded.artprops["Str"] then str = str - wielded.artprops["Str"] end if wielded.artprops["Dex"] then dex = dex - wielded.artprops["Dex"] end if wielded.artprops["Slay"] then it_plus = it_plus - wielded.artprops["Slay"] end end if it.artefact and it.fully_identified then if it.artprops["Str"] then str = str + it.artprops["Str"] end if it.artprops["Dex"] then dex = dex + it.artprops["Dex"] end if it.artprops["Slay"] then it_plus = it_plus + it.artprops["Slay"] end end end if it.is_ranged or it.weap_skill:find("Blades") then stat = dex else stat = str end local stat_mod = 0.75 + 0.025 * stat local skill_mod = (1 + you.skill(it.weap_skill)/25/2) * (1 + you.skill("Fighting")/30/2) local pre_brand_dmg = it.damage * stat_mod * skill_mod + it_plus + get_slay_bonuses() if it.class(true) == "magical staff" then return (pre_brand_dmg + get_staff_bonus_dmg(it, no_brand_dmg)) / get_weap_delay(it) end local ego = it.ego() if not ego then return pre_brand_dmg / get_weap_delay(it) end if ego == "spectralizing" then return 2 * pre_brand_dmg / get_weap_delay(it) end if ego == "vorpality" then if it.is_ranged then return 1.2 * pre_brand_dmg / get_weap_delay(it) end return 7/6 * pre_brand_dmg / get_weap_delay(it) end if not no_brand_dmg then if ego == "flaming" or ego == "freezing" then return 1.25 * pre_brand_dmg / get_weap_delay(it) end if ego == "draining" then return (1.25 * pre_brand_dmg + 2) / get_weap_delay(it) end if ego == "electrocution" then return (pre_brand_dmg + 3.5) / get_weap_delay(it) end -- Ballparking venom as 5 dmg since it totally breaks the paradigm if ego == "venom" then return (pre_brand_dmg + 5) / get_weap_delay(it) end if ego == "pain" then return (pre_brand_dmg + you.skill("Necromancy")/2) / get_weap_delay(it) end -- Distortion does 5.025 extra dmg, + 5% chance to banish if ego == "distortion" then return (pre_brand_dmg + 6) / get_weap_delay(it) end -- Weighted average of all the easily computed brands was ~ 1.17*dmg + 2.13 if ego == "chaos" then return (1.25 * pre_brand_dmg + 2) / get_weap_delay(it) end end return pre_brand_dmg / get_weap_delay(it) end ----------------------------- ---- Weapon info strings ---- ----------------------------- function get_weapon_info(it) if not it.weap_skill then return end local dps = get_weap_dps(it) local dps_str = string.format("%.2f", dps) local it_plus = it.plus if not it_plus then it_plus = 0 end local acc = it.accuracy + it_plus if acc >= 0 then acc = "+"..acc end dps_str = "DPS="..dps_str if dps < 10 then dps_str = dps_str.." " end return dps_str.."(Acc"..acc..")" end function get_armour_info(it) if not it or it.class(true) ~= "armour" then return end if it.subtype() == "shield" then local ev = get_shield_penalty(it) local ev_str = string.format("%.1f", ev) ev_str = "-"..ev_str local sh = get_shield_sh(it) local sh_str = string.format("%.1f", sh) if sh >= 0 then sh_str = "+"..sh_str end sh_str = "SH"..sh_str.."," if sh < 10 then sh_str = sh_str.." " end return sh_str.."EV"..ev_str else local ac = get_armour_ac(it) local ac_str = string.format("%.1f", ac) if ac >= 0 then ac_str = "+"..ac_str end if it.subtype() ~= "body" then return "AC"..ac_str end ac_str = "AC"..ac_str.."," if ac < 10 then ac_str = ac_str.." " end local ev = get_armour_ev(it) local ev_str = string.format("%.1f", ev) if ev >= 0 then ev_str = "+"..ev_str end return ac_str.."EV"..ev_str end end } ########pickup-alert/pa-data.rc######## { ------------------------------------------------------------------------------- ---------------------------- Begin persistant data ---------------------------- ------------------------------------------------------------------------------- if not added_persistant_data or you.turns() == 0 then added_persistant_data = 1 items_picked = { "" } items_alerted = { "" } rare_items = {"broad axe", "executioner's axe", "eveningstar", "demon whip", "sacred scourge", "lajatang", "bardiche", "demon trident", "trishula", "quick blade", "demon blade", "double sword", "triple sword", "eudemon blade", "crystal plate armour", "gold dragon scales", "pearl dragon scales", "storm dragon scales", "shadow dragon scales", "triple crossbow", "hand crossbow", "buckler", "kite shield", "tower shield" } armour_high_score = 0 local eq = items.equipped_at("body armour") if eq then armour_high_score = get_armour_ac(eq) end alerted_first_ranged_one_handed = 0 alerted_first_ranged_two_handed = 0 polearm_high_score = 0 polearm_onehand_high_score = 0 unbranded_high_score = 0 weapon_high_score = 0 eq = items.equipped_at("weapon") if eq then local eq_dmg = get_weap_dps(eq) + eq.accuracy + eq.plus weapon_high_score = eq_dmg unbranded_high_score = get_weap_dps(eq, true) + eq.accuracy + eq.plus if eq.is_ranged then if get_hands(eq) == 1 then alerted_first_ranged_one_handed = 1 else alerted_first_ranged_two_handed = 1 end elseif eq.weap_skill == "Polearms" then polearm_high_score = eq_dmg if get_hands(eq) == 1 then polearm_onehand_high_score = eq_dmg end end end end local function persist_table(table_name, table) local cmd = table_name.." = {" local len = 0 for _,v in ipairs(table) do if v ~= "" then cmd = cmd .. "\"" .. v .. "\", " len = len+1 end end return cmd .. "\"\"}" .. string.char(10) end local function persist_var(var_name, var) return var_name .. " = " .. var .. string.char(10) end if not added_persistant_data_hooks then added_persistant_data_hooks = true table.insert(chk_lua_save, function() return persist_table("rare_items", rare_items) end) table.insert(chk_lua_save, function() return persist_table("items_picked", items_picked) end) table.insert(chk_lua_save, function() return persist_table("items_alerted", items_alerted) end) table.insert(chk_lua_save, function() return persist_var("armour_high_score", armour_high_score) end) table.insert(chk_lua_save, function() return persist_var("alerted_first_ranged_one_handed", alerted_first_ranged_one_handed) end) table.insert(chk_lua_save, function() return persist_var("alerted_first_ranged_two_handed", alerted_first_ranged_two_handed) end) table.insert(chk_lua_save, function() return persist_var("polearm_high_score", polearm_high_score) end) table.insert(chk_lua_save, function() return persist_var("polearm_onehand_high_score", polearm_onehand_high_score) end) table.insert(chk_lua_save, function() return persist_var("unbranded_high_score", unbranded_high_score) end) table.insert(chk_lua_save, function() return persist_var("weapon_high_score", weapon_high_score) end) table.insert(chk_lua_save, function() return persist_var("added_persistant_data", 1) end) end ---------------------------------------- ---- Accessors into persistant data ---- ---------------------------------------- function pickup_item(it) local ret_val = false local name = it.name("plain") if not it.fully_identified then name = "+0 " .. name end if it.artefact then ret_val = true elseif not util.contains(items_picked, name) then -- Add to the table on pickup, in c_assign_invletter() hook function ret_val = true end -- Remove from rare_items if it's there for i,v in ipairs(rare_items) do if v ~= "" and name:find(v) then rare_items[i] = "" return true end end -- Returns true if we haven't picked up this item previously return ret_val end function check_high_scores(it) local ret_val = nil if it.class(true) == "armour" then local ac = get_armour_ac(it) if ac > armour_high_score then armour_high_score = ac if not ret_val then ret_val = "Strongest armour" end end elseif it.class(true) == "weapon" then local it_plus = it.plus if not it_plus then it_plus = 0 end local score = get_weap_dps(it) + (it.accuracy + it_plus) / 2 if score > weapon_high_score then weapon_high_score = score if not ret_val then ret_val = "Good weapon" end end local unbranded_score = get_weap_dps(it, false) + (it.accuracy + it_plus) / 2 if unbranded_score > unbranded_high_score then unbranded_high_score = score if not ret_val then ret_val = "High pure damage" end end if it.weap_skill == "Polearms" then if score > polearm_high_score then polearm_high_score = score if not items.equipped_at("shield") and not ret_val then ret_val = "Good polearm" end end if get_hands(it) == 1 and score > polearm_onehand_high_score then polearm_onehand_high_score = score if not ret_val then ret_val = "1-handed polearm" end end end end return ret_val end function c_assign_invletter_item_alerts(it) if it.weap_skill or it.class(true) == "armour" then local name = it.name("plain") if not util.contains(items_picked, name) then insert_item_and_less_enchanted(items_picked, name) insert_item_and_less_enchanted(items_alerted, name) check_high_scores(it) for i, v in ipairs(rare_items) do if name:find(v) then rare_items[i] = "" end end end end end function alert_item(it, alert_type) local name = it.name("plain") if (it.artefact or it.branded) and not it.fully_identified then if util.contains(items_alerted, name) then return end table.insert(items_alerted, name) show_alert_msg("Item alert, "..alert_type..": ", name) return end -- Prefix unidentified items with "+0" if not it.fully_identified then name = "+0 " .. name end if not util.contains(items_alerted, name) then if it.class(true) == "weapon" or it.class(true) == "magical staff" then show_alert_msg("Item alert, "..alert_type..": ", name.." "..get_weapon_info(it)) elseif it.class(true) == "armour" then show_alert_msg("Item alert, "..alert_type..": ", name.." "..get_armour_info(it)) else show_alert_msg("Item alert, "..alert_type..": ", name) end insert_item_and_less_enchanted(items_alerted, name) end -- Returns true to make other code more concise; indicates that we tried to alert this item return true end crawl.setopt("runrest_stop_message += Item alert, ") function get_rare_item_index(it) local qualname = it.name("qual") for i,v in ipairs(rare_items) do if v ~= "" and qualname:find(v) then return i end end return -1 end ----------------------------------------------------------------------------- ---------------------------- End persistant data ---------------------------- ----------------------------------------------------------------------------- } ########pickup-alert/pa-main.rc######## { ------------------------------------ ---- Autopickup and alerts hook ---- ------------------------------------ add_autopickup_func(function (it, name) local class = it.class(true) -- Check for pickup local retVal = false if class == "armour" and loaded_pa_armour then retVal = pickup_armour(it) elseif class == "weapon" and loaded_pa_weapons then retVal = pickup_weapons(it) elseif class == "magical staff" and loaded_pa_misc then retVal = pickup_staves(it) end if retVal == true then return pickup_item(it) end -- Not picking up this item. Check for alerts if it.name("base") == "orb" and loaded_pa_misc then alert_orbs(it) elseif class == "armour" and loaded_pa_armour then alert_armour(it) elseif class == "weapon" and loaded_pa_weapons then alert_weapons(it) end if loaded_pa_misc then alert_rare_items(it) if class == "magical staff" then alert_staves(it) end end end) ------------------------------- ---- End autopickup master ---- ------------------------------- } ########pickup-alert/pa-armour.rc######## { loaded_pa_armour = true --------------------------------------- ---- Begin alert_armour_upgrades() ---- --------------------------------------- -- If training armour in early/mid game, alert user to any armour that is the strongest found so far local function alert_armour_upgrades(it) if you.skill("Armour") == 0 then return false end if you.xl() > 12 then return false end if (it.artefact or it.branded) and not it.fully_identified then return false end local st, _ = it.subtype() if st ~= "body" then return false end if armour_high_score == 0 then local cur = items.equipped_at("body armour") if not cur then return false end armour_high_score = get_armour_ac(cur) else local itAC = get_armour_ac(it) if itAC > armour_high_score+0.3 then armour_high_score = itAC return alert_item(it, "Stronger armour") end end return false end ------------------------------------- ---- End alert_armour_upgrades() ---- ------------------------------------- ------------------------------- ---- Begin pickup_armour() ---- ------------------------------- -- Equipment autopickup (by Medar, gammafunk, sockthot, and various others) function pickup_armour(it) if it.is_useless then return false end local st, _ = it.subtype() if st == "body" then -- Body armour: AC upgrades, new egos that don't lose AC, and artefacts that don't lose 5+ AC local cur = items.equipped_at("body armour") if not cur then return false end if not it.fully_identified and it.branded then return false end if it.encumbrance <= cur.encumbrance then local ac_delta = get_armour_ac(it) - get_armour_ac(cur) if it.artefact and ac_delta > -5 then return true end if cur.artefact then return false end if it.ego() == cur.ego() and (has_ego(it) or not has_ego(cur)) then if ac_delta > 0 then return true end return false elseif has_ego(it) and not (cur.artefact or has_ego(cur)) then if ac_delta >= 0 then return true end end end elseif st == "shield" then -- Shields if not it.fully_identified or it.name("base") == "orb" then return false end local cur = items.equipped_at("shield") if not cur then return false end if cur.name("base") ~= it.name("base") then return false end if it.artefact then return true end if cur.artefact then return false end if cur.branded then if cur.ego() == it.ego() then return it.plus > cur.plus end return false end if it.branded then return true end return it.plus > cur.plus else -- Aux armour: Pickup artefacts, AC upgrades, and new egos -- No pickup if mutation interference if st == "gloves" then -- Ignore demonic touch if you're wearing a shield if not items.equipped_at("shield") then if you.get_base_mutation_level("demonic touch", true, false, true) > 0 then return false end end -- Ignore claws if you're wielding a weapon if not items.equipped_at("weapon") then if you.get_base_mutation_level("claws", true, false, true) > 0 then return false end end elseif st == "boots" then if you.get_base_mutation_level("hooves", true, false, true) > 0 then return false end if you.get_base_mutation_level("talons", true, false, true) > 0 then return false end elseif it.name("base"):find("helmet") then if you.get_base_mutation_level("horns", true, false, true) > 0 then return false end if you.get_base_mutation_level("beak", true, false, true) > 0 then return false end if you.get_base_mutation_level("antennae", true, false, true) > 0 then return false end end if it.artefact then return true end local cur = items.equipped_at(st) if not cur then return true end if not it.fully_identified then return false end if it.branded then if it.ego() ~= cur.ego() then return true end if get_armour_ac(it) > get_armour_ac(cur) then return true end else if cur.branded or cur.artefact then return false end if get_armour_ac(it) > get_armour_ac(cur) then return true end end end return false end ----------------------------- ---- End pickup_armour() ---- ----------------------------- -------------------------------------------- ---- Begin alert_armour_while_mutated() ---- -------------------------------------------- -- Special cases where you have temporary or innate mutations that interfere with armour local function alert_armour_while_mutated(it, type) -- Alerts usable ego items if there are mutations -- Returns before alerting if innate mutations make item unusable -- Returns true iff there are mutations if type == "gloves" then local claws_lvl_innate = you.get_base_mutation_level("claws", true, false, false) if claws_lvl_innate >= 3 then return true end local touch_lvl_innate = you.get_base_mutation_level("demonic touch", true, false, false) if touch_lvl_innate >= 3 then return true end local claws_lvl = you.get_base_mutation_level("claws", true, false, true) local touch_lvl = you.get_base_mutation_level("demonic touch", true, false, true) if claws_lvl + touch_lvl > 0 then if it.artefact or it.branded then return alert_item(it, "Branded gloves") end return true end elseif type == "boots" then local hooves_lvl_innate = you.get_base_mutation_level("hooves", true, false, false) if hooves_lvl_innate >= 3 then return true end local talons_lvl_innate = you.get_base_mutation_level("talons", true, false, false) if talons_lvl_innate >= 3 then return true end local hooves_lvl = you.get_base_mutation_level("hooves", true, false, true) local talons_lvl = you.get_base_mutation_level("talons", true, false, true) if hooves_lvl + talons_lvl > 0 then if it.artefact or it.branded then return alert_item(it, "Branded boots") end return true end elseif type == "helmet" then local horns_lvl_innate = you.get_base_mutation_level("horns", true, false, false) local antennae_lvl_innate = you.get_base_mutation_level("antennae", true, false, false) if it.name("base"):find("helmet") then if horns_lvl_innate > 0 then return true end if antennae_lvl_innate > 0 then return true end if you.get_base_mutation_level("beak", true, false, false) > 0 then return true end else -- hat/crown/etc if horns_lvl_innate >= 3 then return true end if antennae_lvl_innate >= 3 then return true end end local horns_lvl = you.get_base_mutation_level("horns", true, false, true) local antennae_lvl = you.get_base_mutation_level("antennae", true, false, true) local beak_lvl = you.get_base_mutation_level("beak", true, false, true) if horns_lvl + antennae_lvl + beak_lvl > 0 then if it.artefact or it.branded then return alert_item(it, "Branded headgear") end return true end end return false end -------------------------------------------- ---- End alert_armour_while_mutated() ---- -------------------------------------------- ------------------------------------------ ---- Begin alert_interesting_armour() ---- ------------------------------------------ local function alert_interesting_armour(it) if it.artefact then if not it.fully_identified then return false end return alert_item(it, "Artefact armour") end local st, _ = it.subtype() if st == "body" then local cur = items.equipped_at("body armour") if not cur then return false end if it.encumbrance == cur.encumbrance then if has_ego(it) then if not has_ego(cur) then return alert_item(it, "Added ego") end if it.ego() ~= cur.ego() then return alert_item(it, "New ego") end end if get_armour_ac(it) > get_armour_ac(cur) then return alert_item(it, "Stronger armour1") end elseif it.encumbrance < cur.encumbrance then -- Lighter armour local ev_gain = get_armour_ev(it) - get_armour_ev(cur) local ac_lost = get_armour_ac(cur) - get_armour_ac(it) if has_ego(it) then if not cur.artefact and not has_ego(cur) then if ev_gain >= 0.6 * ac_lost or ac_lost <= 6 then return alert_item(it, "Added ego1") end elseif it.ego() ~= cur.ego() then if ev_gain >= 0.8 * ac_lost or ac_lost <= 6 then return alert_item(it, "New ego1") end else -- Same ego if ev_gain >= 1.2 * ac_lost then return alert_item(it, "Lighter armour1") end end else if cur.artefact or has_ego(cur) then -- Lost ego if ev_gain >= 2 * ac_lost then return alert_item(it, "Lighter armour2") end else -- Neither has ego if ev_gain >= 1.2 * ac_lost then return alert_item(it, "Lighter armour3") end end end else -- Heavier armour local ac_gain = get_armour_ac(it) - get_armour_ac(cur) local ev_lost = get_armour_ev(cur) - get_armour_ev(it) local encumb_penalty = 0 if you.skill("Spellcasting") > 1 then encumb_penalty = (it.encumbrance - cur.encumbrance)/2 end if has_ego(it) then if not cur.artefact and not has_ego(cur) then if ac_gain >= 0.4 * ev_lost or ev_lost <= 8 then return alert_item(it, "Added ego2") end elseif it.ego() ~= cur.ego() then if ac_gain >= 0.6 * ev_lost or ev_lost <= 8 then return alert_item(it, "New ego2") end else -- Same ego if ac_gain >= 0.8 * ev_lost + encumb_penalty then return alert_item(it, "Stronger armour2") end end else if cur.artefact or has_ego(cur) then -- Lost ego if ac_gain >= 1 * ev_lost + encumb_penalty then return alert_item(it, "Stronger armour3") end else -- Neither has ego if ac_gain >= 0.8 * ev_lost + encumb_penalty then return alert_item(it, "Stronger armour4") end end end end elseif st == "shield" then local cur = items.equipped_at("shield") if not cur then return false end if it.branded and it.ego() ~= cur.ego() then return alert_item(it, "New egoS") end else -- Aux armour local cur = items.equipped_at(st) if cur and not it.is_useless and get_armour_ac(it) > get_armour_ac(cur) then return alert_item(it, "Stronger armour5") end return alert_armour_while_mutated(it, st) end end ---------------------------------------- ---- End alert_interesting_armour() ---- ---------------------------------------- function alert_armour(it) if not it.is_useless then alert_armour_upgrades(it) end if it.fully_identified or not it.branded then alert_interesting_armour(it) end end } ########pickup-alert/pa-weapons.rc######## { loaded_pa_weapons = true -------------------------- ---- Begin inv arrays ---- -------------------------- -- Use these arrays to compare potential upgrades against entire inventory local top_school = "unarmed combat" local all_schools = { "axes", "maces & flails", "long blades", "short blades", "staves", "unarmed combat", "ranged weapon" } local function set_top_school() local max = 0 for _, v in ipairs(all_schools) do if you.skill(v) > max then max = you.skill(v) top_school = v end end end local inv_max_dmg = { melee = 0, melee_b = 0, twohands = 0, twohands_b = 0, ranged = 0, ranged_b = 0 } local inv_max_dmg_acc = { melee = 0, melee_b = 0, twohands = 0, twohands_b = 0, ranged = 0, ranged_b = 0 } local egos = { } local function get_weap_type(it) if it.is_ranged then if it.branded then return "ranged_b" else return "ranged" end else if get_hands(it) == 2 then if it.branded then return "twohands_b" else return "twohands" end else if it.branded then return "melee_b" else return "melee" end end end end local function enforce_dmg_floor(target, floor) if inv_max_dmg[target] < inv_max_dmg[floor] then inv_max_dmg[target] = inv_max_dmg[floor] inv_max_dmg_acc[target] = inv_max_dmg_acc[floor] end end local function generate_inv_weap_arrays() set_top_school() for cur in iter.invent_iterator:new(items.inventory()) do if cur.class(true) == "weapon" then if cur.ego() then table.insert(egos, cur.ego()) end local dmg = get_weap_dps(cur) local weap_type = get_weap_type(cur) if dmg > inv_max_dmg[weap_type] then inv_max_dmg[weap_type] = dmg inv_max_dmg_acc[weap_type] = cur.accuracy + cur.plus end end end enforce_dmg_floor("melee", "melee_b") enforce_dmg_floor("twohands_b", "melee_b") enforce_dmg_floor("twohands", "twohands_b") enforce_dmg_floor("twohands", "melee") enforce_dmg_floor("ranged", "ranged_b") end ------------------------ ---- End inv arrays ---- ------------------------ ------------------------------- ---- alert_early_weapons() ---- ------------------------------- -- Alert strong weapons early local function alert_early_weapons(it) -- Alert really good usable ranged weapons if you.xl() <= 14 then if it.fully_identified and it.is_ranged then if it.branded and it.plus >= 5 or it.plus >= 7 then if get_hands(it) == 1 or not items.equipped_at("shield") or you.skill("shield") <= 8 then return alert_item(it, "Ranged weapon9") end end end end -- Skip items when we're clearly going another route if you.skill(top_school) - you.skill(it.weap_skill) > 1.5*you.xl()+3 then return end if you.xl() <= 8 then if it.branded or it.plus and it.plus >= 4 then -- Make sure we don't alert a pure downgrade to something in inventory for inv in iter.invent_iterator:new(items.inventory()) do if inv.name("base") == it.name("base") then if inv.plus >= it.plus then if not it.branded then return end if it.ego() == inv.ego() then return end end end end return alert_item(it, "Early weapon") end end end ---------------------------- ---- Alert first ranged ---- ---------------------------- local function alert_first_ranged(it) if not it.is_ranged then return false end if get_hands(it) == 2 then if items.equipped_at("shield") then return false end if alerted_first_ranged_two_handed == 0 then alerted_first_ranged_two_handed = 1 for inv in iter.invent_iterator:new(items.inventory()) do if inv.is_ranged and get_hands(inv) == 2 then return true end end return alert_item(it, "Ranged weapon5") end else if alerted_first_ranged_one_handed == 0 then alerted_first_ranged_one_handed = 1 for inv in iter.invent_iterator:new(items.inventory()) do if inv.is_ranged then return true end end return alert_item(it, "Ranged weapon6") end end return false end -------------------------------- ---- Begin pickup_weapons() ---- -------------------------------- local function pickup_weapon(it, cur) if cur.subtype() == it.subtype() then -- Exact weapon type match if it.artefact then return true end if cur.artefact then return false end if it.branded and it.fully_identified and not cur.branded then return true end if cur.branded and not it.branded then return false end return it.ego() == cur.ego() and get_weap_dps(it) > get_weap_dps(cur) elseif you.skill(it.weap_skill) >= 0.5 * you.skill(cur.weap_skill) then -- A usable weapon school -- Replace cur weapon if new one does more damage, unless there are potential benefits to cur if get_hands(it) > get_hands(cur) then return false end if it.is_ranged ~= cur.is_ranged then return false end if cur.weap_skill == "Polearms" and it.weap_skill ~= "Polearms" then return false end if it.artefact then return true end if cur.artefact then return false end if it.branded and not it.fully_identified then return false end if cur.branded and not it.branded then return false end local dmg_delta if get_weap_dps(cur) > inv_max_dmg[get_weap_type(it)] then dmg_delta = get_weap_dps(it) - get_weap_dps(cur) else dmg_delta = get_weap_dps(it) - inv_max_dmg[get_weap_type(it)] end if dmg_delta > 0 then return true end local it_plus = it.plus if not it_plus then it_plus = 0 end return dmg_delta == 0 and (it.accuracy+it_plus) > inv_max_dmg_acc[get_weap_type(it)] end return false end function pickup_weapons(it) if it.is_useless then return false end generate_inv_weap_arrays() local unarmed = true for cur in iter.invent_iterator:new(items.inventory()) do if cur.class(true) == "weapon" then if pickup_weapon(it, cur) then return true end if not cur.is_ranged then unarmed = false end end end -- Return false unless you need a weapon but don't have one yet if not unarmed or you.skill("Unarmed Combat") > 0 then return false end if you.get_base_mutation_level("claws", true, false, true) > 0 then return false end if you.get_base_mutation_level("demonic touch", true, false, true) > 0 then return false end return true end ------------------------------ ---- End pickup_weapons() ---- ------------------------------ ------------------------------------------- ---- Begin alert_interesting_weapons() ---- ------------------------------------------- local function alert_interesting_weapon(it, cur) if it.artefact and it.fully_identified then return alert_item(it, "Artefact weapon") end if cur.subtype() == it.subtype() then -- Exact weapon type match if it.branded and it.ego() ~= cur.ego() then return alert_item(it, "New ego1") end if get_weap_dps(it) > inv_max_dmg[get_weap_type(it)] then return alert_item(it, "Stronger weapon0") end elseif you.skill(it.weap_skill) >= 0.5 * you.skill(cur.weap_skill) then -- A usable weapon school if it.is_ranged ~= cur.is_ranged then return false end --local penalty = 1 --if it.weap_skill == top_school then penalty = 0.5 end local penalty = 0.5 * you.skill(cur.weap_skill) / you.skill(it.weap_skill) if get_hands(it) == 2 and get_hands(cur) == 1 then -- Item requires an extra hand if it.branded and not cur.artefact and not cur.branded then if get_weap_dps(it) > 0.8*get_weap_dps(cur) then return alert_item(it, "2-handed weapon1") end end if not items.equipped_at("shield") then if it.branded and it.ego() ~= cur.ego() then return alert_item(it, "New ego") end if not (cur.branded or cur.artefact) and get_weap_dps(it) > inv_max_dmg[get_weap_type(it)] then return alert_item(it, "2-handed weapon2") end if (cur.branded or cur.artefact) and not it.branded and get_weap_dps(it) > inv_max_dmg[get_weap_type(it)] then return alert_item(it, "2-handed weapon3") end elseif you.skill("shields") <= 4 then -- Not really training shields; may be interested in big upgrades if get_weap_dps(it) >= penalty*2*inv_max_dmg["melee"] then return alert_item(it, "2-handed weapon4") end end else -- Item uses same number of hands or fewer if cur.artefact then return false end if it.branded then local dmg_delta = get_weap_dps(it) - inv_max_dmg[get_weap_type(it)] if not cur.branded then if dmg_delta >= -4/penalty then return alert_item(it, "New ego2") end elseif it.ego() == cur.ego() then if dmg_delta >= 0 then return alert_item(it, "Stronger weapon1") end elseif not util.contains(egos, it.ego()) then if dmg_delta >= -4/penalty then return alert_item(it, "New ego3") end end else -- Not branded if cur.branded then if get_weap_dps(it) > inv_max_dmg[get_weap_type(it)] then return alert_item(it, "Stronger weapon2") end else local dmg_delta, other_acc if get_weap_dps(cur) > inv_max_dmg[get_weap_type(it)] then dmg_delta = get_weap_dps(it) - get_weap_dps(cur) other_acc = cur.accuracy + cur.plus else dmg_delta = get_weap_dps(it) - inv_max_dmg[get_weap_type(it)] other_acc = inv_max_dmg_acc[get_weap_type(it)] end if dmg_delta > 0 then return alert_item(it, "Stronger weapon3") end local it_plus = it.plus if not it_plus then it_plus = 0 end if dmg_delta == 0 and (it.accuracy+it_plus) > other_acc then return alert_item(it, "Higher accuracy") end end end end end end local function alert_interesting_weapons(it) generate_inv_weap_arrays() local ranged_weap_in_inv = false for cur in iter.invent_iterator:new(items.inventory()) do if cur.class(true) == "weapon" then if alert_interesting_weapon(it, cur) then return true end if cur.is_ranged then ranged_weap_in_inv = true end end end -- Alert for the first ranged weapon found (for 1 and 2 handed separately) if it.is_ranged and not ranged_weap_in_inv then if it.artefact or it.branded and it.plus >= 4 then if items.equipped_at("shield") then local qualname = it.name("qual") if qualname:find("hand crossbow") or qualname:find("sling") then return alert_item(it, "Ranged Weapon1") end else return alert_item(it, "Ranged Weapon2") end end end return false end ----------------------------------------- ---- End alert_interesting_weapons() ---- ----------------------------------------- local function alert_high_scores(it) local category = check_high_scores(it) if category then alert_item(it, category) end end function alert_weapons(it) if it.is_useless then return end if (it.artefact or it.branded) and not it.fully_identified then return end alert_interesting_weapons(it) alert_first_ranged(it) alert_early_weapons(it) alert_high_scores(it) end } ########pickup-alert/pa-misc.rc######## { loaded_pa_misc = true -------------------------- ---- Alert rare items ---- -------------------------- function alert_rare_items(it) local index = get_rare_item_index(it) if index == -1 then return end local do_alert = true -- Don't alert if already wearing a larger shield if rare_items[index] == "buckler" then local sh = items.equipped_at("shield") if sh and sh.name("base") ~= "orb" then do_alert = false end elseif rare_items[index] == "kite shield" then local sh = items.equipped_at("shield") if sh and sh.name("base"):find("tower shield") then do_alert = false end end if do_alert then -- Found a new rare item! We may already have one if it was auto-picked up show_alert_msg("It's your first ", rare_items[index].."!") crawl.more() end rare_items[index] = "" return end crawl.setopt("runrest_stop_message += It's your first.*!") -------------------- ---- Alert orbs ---- -------------------- function alert_orbs(it) if items.equipped_at("shield") then return false end if not it.fully_identified then return false end alert_item(it, "New orb") return false end ---------------------------- ---- Smart staff pickup ---- ---------------------------- function pickup_staves(it) if it.is_useless or not it.fully_identified then return false end local basename = it.name("base") local good_staff = false if basename == "staff of fire" then good_staff = you.skill("Fire Magic") > 0 elseif basename == "staff of cold" then good_staff = you.skill("Ice Magic") > 0 elseif basename == "staff of earth" then good_staff = you.skill("Earth Magic") > 0 elseif basename == "staff of air" then good_staff = you.skill("Air Magic") > 0 elseif basename == "staff of poison" then good_staff = you.skill("Poison Magic") > 0 elseif basename == "staff of death" then good_staff = you.skill("Necromancy") > 0 elseif basename == "staff of conjuration" then good_staff = you.skill("Conjurations") > 0 else crawl.mpr("Unrecognized staff: " .. basename, "error") end if good_staff == true then return true end return false end ------------------------ ---- alert_staves() ---- ------------------------ function alert_staves(it) if not it.fully_identified then return false end local needRes = false local basename = it.name("base") if basename == "staff of fire" then needRes = you.res_fire() == 0 elseif basename == "staff of cold" then needRes = you.res_cold() == 0 elseif basename == "staff of air" then needRes = you.res_shock() == 0 elseif basename == "staff of poison" then needRes = you.res_poison() == 0 elseif basename == "staff of death" then needRes = you.res_draining() == 0 end if needRes == true then return alert_item(it, "Staff resistance") end return false end } ### Features ### ########features/inscribe-stats.rc######## { local function inscribe_armour_stats(it) local new_inscr = get_armour_info(it) local idx if it.subtype() == "shield" then idx = it.inscription:find("SH+") else idx = it.inscription:find("AC+") end if idx then if idx + #new_inscr <= #it.inscription then new_inscr = new_inscr..it.inscription:sub(idx + #new_inscr, #it.inscription) end if idx > 1 then new_inscr = it.inscription:sub(1, idx-1)..new_inscr end end it.inscribe(new_inscr, false) return new_inscr end local function inscribe_weapon_stats(it) local new_inscr = get_weapon_info(it) local idx = it.inscription:find("DPS=") if idx then if idx + #new_inscr <= #it.inscription then new_inscr = new_inscr..it.inscription:sub(idx + #new_inscr, #it.inscription) end if idx > 1 then new_inscr = it.inscription:sub(1, idx-1)..new_inscr end end it.inscribe(new_inscr, false) return new_inscr end ------------------------------------------ ------------------ Hook ------------------ ------------------------------------------ function c_message_inscribe_dps(text, channel) for it in iter.invent_iterator:new(items.inventory()) do local class = it.class(true) if class == "weapon" or class == "magical staff" then inscribe_weapon_stats(it) elseif class == "armour" then inscribe_armour_stats(it) end end end } ########features/remind-id.rc######## { ------------------------------------- ---- Unidentified items reminder ---- ------------------------------------- local function remind_unidentified_items(have_unidentified) for it in iter.invent_iterator:new(items.inventory()) do if not it.fully_identified then for s in iter.invent_iterator:new(items.inventory()) do if s and s.name("qual"):find("scroll of identify") then crawl.mpr("----You have something to identify.----", "plain") end end return end end end crawl.setopt("runrest_stop_message += You have something to identify") --------------------------------------------- ------------------- Hooks ------------------- --------------------------------------------- function c_message_remind_identify(text, channel) if channel ~= "plain" then return end if text:find(" of identify") and not text:find("drop") and not text:find("read") then remind_unidentified_items(false) end end function c_assign_invletter_remind_identify(it) if not it.fully_identified then remind_unidentified_items(true) end end } ########features/weapon-slots.rc######## { ------------------------------ ---- Cleanup weapon slots ---- ------------------------------ --Whenever you drop an item: -- Assign weapons to slots a and b -- Priority: 1:wielded, 2:weapon, not polearm/ranged unless skill -- 3:magical staff, 4:polearm -- Assign weap to w: ranged/polearm/any local function get_first_empty_slot() for slot=1,52 do if not items.inslot(slot) then return slot end end end local function get_priority_ab(it) if not it or not it.weap_skill then return -1 end if it.equipped then return 1 end local class = it.class(true) if class == "magical staff" then return 3 end if class == "weapon" then if it.is_ranged then if you.skill("Ranged Weapons") >= 4 then return 2 end return 5 end if it.weap_skill == "Polearms" then if you.skill("Polearms") >= 4 then return 2 end return 4 end return 2 end return -1 end local function get_priority_w(it) if not it or not it.weap_skill then return -1 end if it.is_ranged then return 1 end if it.weap_skill == "Polearms" then return 2 end return 3 end -------------- ---- main ---- -------------- local priorities_ab = nil local priorities_w = nil local function generate_priorities() priorities_ab = { -1, -1, -1, -1, -1 } priorities_w = { -1, -1, -1 } for it in iter.invent_iterator:new(items.inventory()) do local p = get_priority_w(it) if p > 0 then if priorities_w[p] == -1 then priorities_w[p] = it.slot else priorities_w[p+1] = it.slot end end p = get_priority_ab(it) if p > 0 then if priorities_ab[p] == -1 then priorities_ab[p] = it.slot else priorities_ab[p+1] = it.slot end end end end local function cleanup_w() local slot_w = items.letter_to_index("w") local inv = items.inslot(slot_w) if inv and inv.class(true) == "weapon" then return end for p=1,3 do if priorities_w[p] > 1 then items.swap_slots(priorities_w[p], slot_w) return end end end local function cleanup_ab(ab) local inv inv = items.inslot(ab) if inv and inv.class(true) == "weapon" then return end for p=1,5 do if priorities_ab[p] > ab then items.swap_slots(priorities_ab[p], ab) priorities_ab[p] = -1 return end end end local function cleanup_weapon_slots() generate_priorities() cleanup_ab(0) cleanup_ab(1) cleanup_w() end local do_cleanup_weapon_slots = false --------------------------------------------- ------------------- Hooks ------------------- --------------------------------------------- function c_assign_invletter_weapon_slots(it) if it.class(true) ~= "weapon" and it.class(true) ~= "magical staff" then return end for i=0,2 do local slot if i==2 then slot = items.letter_to_index("w") else slot = i end local inv = items.inslot(slot) if not inv then return slot end if inv.class(true) ~= "weapon" and inv.class(true) ~= "magical staff" then items.swap_slots(slot, get_first_empty_slot()) return slot end end end function c_message_weapon_slots(text, channel) if channel ~= "plain" then return end if not text:find("You drop ") then return end do_cleanup_weapon_slots = true end function ready_weapon_slots() if do_cleanup_weapon_slots then cleanup_weapon_slots() do_cleanup_weapon_slots = false end end } ########features/exclude-dropped.rc######## { -- Add autopickup exclusion for any jewellery/missile/evocable item that is dropped -- Exclusion is removed when you pick the item back up ------------------------- ---- Persistant data ---- ------------------------- if not dropped_item_exclusions or you.turns() == 0 then dropped_item_exclusions = "" end local function persist_dropped_item_exclusions() return "dropped_item_exclusions = \""..dropped_item_exclusions.."\""..string.char(10) end if not added_exclude_dropped_hooks then table.insert(chk_lua_save, persist_dropped_item_exclusions) added_exclude_dropped_hooks = true end local function get_jewellery_name(text) local idx = text:find("ring of ") if not idx then idx = text:find("amulet of ") end if not idx then return end text = text:gsub(" {.*}", "") text = text:gsub("[.]", "") return text:sub(idx,#text) end local all_missiles = { " stone", "poisoned dart", "curare", "atropa", "datura", "boomerang", "javelin" } local function get_missile_name(text) for _, item_name in ipairs(all_missiles) do if text:find(item_name) then if item_name == "boomerang" or item_name == "javelin" then if text:find("silver") then item_name = "silver "..item_name elseif text:find("dispersal") then item_name = item_name.."s? of dispersal" else item_name = "(?"..item_name) if dropped_item_exclusions ~= "" then dropped_item_exclusions = dropped_item_exclusions.."," end else crawl.setopt("autopickup_exceptions -= >"..item_name) -- Remove persistant exclusion (try 3 times to make sure we capture comma) dropped_item_exclusions = dropped_item_exclusions:gsub(",>"..item_name, "") dropped_item_exclusions = dropped_item_exclusions:gsub(">"..item_name..",", "") dropped_item_exclusions = dropped_item_exclusions:gsub(">"..item_name, "") end end if dropped_item_exclusions ~= "" then crawl.setopt("autopickup_exceptions ^= "..dropped_item_exclusions) end } ########features/safe-consumables.rc######## ############################### ####### Auto-inscribe ######### # Unidentified consumables autoinscribe += potions?(?!.*of ):!q autoinscribe += scrolls?(?!.*of ):!r # Add a warning for everything that doesn't have one built-in autoinscribe += potions? of(?!.*(attraction|lignification|mutation)):!q autoinscribe += scrolls? of(butterflies|fear|fog|magic mapping|posion|silence|summoning|teleportation|torment|vulnerability):!r { -------------------------------------------- ---- Protective consumable inscriptions ---- -------------------------------------------- -- Initialize to true to fix inscriptions on init (starting items don't get default inscriptions) local no_inscriptions = {"acquirement", "amnesia", "blinking", "brand weapon", "enchant armour", "enchant weapon", "identify", "immolation", "noise", "poison", "vulnerability", "attraction", "lignification", "mutation" } local function fix_inscriptions() -- Remove the default "!r" and "!q" inscriptions after identify local keystring = "" for it in iter.invent_iterator:new(items.inventory()) do if it.fully_identified then local it_class = it.class(true) if it_class == "potion" or it_class == "scroll" then local qualname = it.name("qual") local st, _ = it.subtype() if util.contains(no_inscriptions, st) then if it.inscription:find("%!r") then it.inscribe(it.inscription:gsub("%!r", ""), false) end if it.inscription:find("%!q") then it.inscribe(it.inscription:gsub("%!q", ""), false) end elseif it_class == "scroll" and not util.contains(no_inscriptions, st) and not qualname:find("!r") then it.inscribe("!r") elseif it_class == "potion" and not util.contains(no_inscriptions, st) and not qualname:find("!q") then it.inscribe("!q") end end end end end local do_fix_inscriptions = true --------------------------------------------- ------------------- Hooks ------------------- --------------------------------------------- function ready_safe_consumables() if do_fix_inscriptions then fix_inscriptions() do_fix_inscriptions = false end end function c_message_safe_consumables(text, channel) if channel ~= "plain" then return end local triggers = { "Thank you for shopping at", "s a scroll of ", "s a potion of ", "read the scroll of identify" } for _, v in ipairs(triggers) do if text:find(v) then fix_inscriptions() do_fix_inscriptions = true return end end end } ########features/drop-inferior.rc######## -- Auto-drop items that are strictly worse than another -- On item pickup, inscribes inferior items with "~drop" drop_filter += ~drop { ------------------------------------------ ------------------ Hook ------------------ ------------------------------------------ function c_assign_invletter_drop_inferior(it) -- Skip brands that are potentially harmful local it_ego = it.ego() if it_ego == "distortion" or it_ego == "chaos" or it_ego == "infusion" then return end local it_class = it.class(true) if it_class ~= "weapon" and it_class ~= "armour" then return end local risky_artefact = false if it.artefact then local qualname = it.name("qual") if qualname:find("%-") or qualname:find("Harm") or qualname:find("Infuse") then risky_artefact = true end end if risky_artefact then return end local st = it.subtype() local basename = it.name("base") for inv in iter.invent_iterator:new(items.inventory()) do local item_match = false if inv.subtype() == st then if st == "body" then if inv.encumbrance >= it.encumbrance then item_match = true end else if inv.name("base") == basename then item_match = true end end end if not inv.artefact and item_match and (not has_ego(inv) or inv.ego() == it.ego()) then if it_class == "weapon" then if inv.plus <= it.plus then inv.inscribe("~drop") end else if inv.ac <= it.ac then inv.inscribe("~drop") end end end end end function c_assign_invletter_exclude_dropped(it) it.inscribe(it.inscription:gsub("~drop", ""), false) end } ########features/runrest-features.rc######## { -------------------------------- ---- Fully rest off effects ---- -------------------------------- crawl.setopt("runrest_ignore_message += recovery:.*") crawl.setopt("runrest_ignore_message += duration:.*") local function fully_recovered() local status_wait = { "on berserk cooldown", "short of breath", "corroded", "vulnerable"} for _, s in ipairs(status_wait) do if you.status(s) then return false end end return true end local waiting_for_recovery = false local explore_after_recovery = false -- Attach full recovery to auto-explore function explore_full_recovery() if not fully_recovered() then explore_after_recovery = true crawl.do_commands({"CMD_REST"}) elseif you.status("slowed") then if not (you.strength() <=0 or you.intelligence() <= 0 or you.dexterity() <=0) then explore_after_recovery = true crawl.do_commands({"CMD_REST"}) else crawl.do_commands({"CMD_EXPLORE"}) end else crawl.do_commands({"CMD_EXPLORE"}) end end crawl.setopt("macros += M o ===explore_full_recovery") ------------------------------------- ---- Hooks (All functions below) ---- ------------------------------------- -------------------------------- ---- rest off effects cont. ---- -------------------------------- function ch_stop_running_full_recovery(kind) if kind == "run" and not fully_recovered() then waiting_for_recovery = true crawl.setopt("message_colour += mute:You start waiting.") end end function c_message_fully_recover(text, channel) --if text:find("contamination") then crawl.mpr(text) crawl.mpr(channel) end if text:find("You start waiting.") then if not fully_recovered() then waiting_for_recovery = true crawl.setopt("message_colour += mute:You start waiting.") end elseif waiting_for_recovery then you.stop_activity() end end function ready_fully_recover() if waiting_for_recovery then if fully_recovered() then you.stop_activity() crawl.setopt("message_colour -= mute:You start waiting.") waiting_for_recovery = false crawl.mpr("Fully recovered.") if explore_after_recovery then crawl.sendkeys("o") explore_after_recovery = false end else crawl.do_commands({"CMD_WAIT"}) end end end -------------------------------------------------------------------- ---- End Fully rest off effects (Hooks for other features cont. ---- -------------------------------------------------------------------- ---------------------- ---- Ignore altars ---- ----------------------- local stop_on_altars = true function ready_ignore_altars() if stop_on_altars and (you.god() ~= "No God" or you.branch() == "Temple") then stop_on_altars = false crawl.setopt("explore_stop -= altars") elseif not stop_on_altars and you.god() == "No God" and you.branch() ~= "Temple" then stop_on_altars = true crawl.setopt("explore_stop += altars") end end ----------------------------------------------- ---- Search altars in temple after explore ---- ----------------------------------------------- function c_message_search_altars_in_temple(text, channel) if you.branch() == "Temple" and text:find("explor") then crawl.sendkeys({ 6, "altar\r" }) end end ----------------------------- ---- Ignore exit portals ---- ----------------------------- local ignore_exit_brances = { "Bailey", "Bazaar", "Ice Cave", "Ossuary", "Sewer", "Trove", "Volcano", "Ziggurat" } local stop_on_portals = true function ready_ignore_exits() local branch = you.branch() if stop_on_portals and util.contains(ignore_exit_brances, branch) then stop_on_portals = false crawl.setopt("explore_stop -= portals") elseif not stop_on_portals and not util.contains(ignore_exit_brances, branch) then stop_on_portals = true crawl.setopt("explore_stop += portals") end end } ########features/mute-swaps.rc######## { -- When an item is moved to its assigned slot, mute the messages for the item that was previously in that slot -- If we cared what slot the item was in, it'd already be assigned somewhere -- This mostly matters when reading scroll of ID, where 5-6 lines of inventory items can be confusing -------------- ---- Util ---- -------------- local function cleanup_message(text) local tags_removed = {} local keep_going = true while keep_going do local opening = text:find("<") local closing = text:find(">") if opening and closing and opening < closing then local new_text = "" if opening > 1 then new_text = text:sub(1, opening-1) end if closing < #text then new_text = new_text..text:sub(closing+1, #text) end text = new_text else keep_going = false end end text = text:gsub("\n", "") return text end muted_items = {} -- Must define this separate from ready() if we want to call it from c_message_mute_swaps as well local function unmute_items() for _, v in ipairs(muted_items) do crawl.setopt("message_colour -= mute: - "..v) end muted_items = {} end --------------------------------------------- ------------------- Hooks ------------------- --------------------------------------------- function ready_mute_swaps() unmute_items() end local last_pickup_turn = -1 function c_assign_invletter_mute_swaps(it) -- this causes an unmute command on the message -- we can't unmute in time from this hook if you.turns() == last_pickup_turn then crawl.mpr("") else last_pickup_turn = you.turns() end end function c_message_mute_swaps(text, channel) -- Mute subsequent item re-assignments in a single turn, for everything after the first item. -- Multiple slots for the same item will still be shown if channel == "plain" then text = cleanup_message(text) if text:sub(2,4) == " - " then local item = text:sub(5, #text) local mute_str = "(?!.*("..item.."))" table.insert(muted_items, mute_str) crawl.setopt("message_colour ^= mute: - "..mute_str) return end end unmute_items() end } ########features/after-shaft.rc######## { ------------------------------------ ---- Stop on stairs after shaft ---- ------------------------------------ if not shaft_depth or you.turns() == 0 then shaft_depth = 0 shaft_branch = "NA" end function persist_shaft_values() local cmd = "shaft_depth = "..shaft_depth..string.char(10) .."shaft_branch = \""..shaft_branch.."\""..string.char(10) return cmd end if not added_shaft_stairs_hook then table.insert(chk_lua_save, persist_shaft_values) added_shaft_stairs_hook = true end --------------------------------------------- ------------------- Hooks ------------------- --------------------------------------------- function c_message_after_shaft(text, channel) if shaft_depth ~= 0 then return end if text:find("fall into a shaft") then shaft_depth = you.depth() shaft_branch = you.branch() crawl.setopt("explore_stop += stairs") end end function ready_after_shaft() if shaft_depth ~= 0 then if you.depth() == shaft_depth and you.branch() == shaft_branch then crawl.setopt("explore_stop -= stairs") shaft_depth = 0 shaft_branch = "NA" end end end } #################################################################### ######################## Lua Hook Functions ######################## #################################################################### { ---------------------------------- ---------- c_message() ----------- ---------------------------------- function c_message(text, channel) if c_message_remind_identify then c_message_remind_identify(text, channel) end if c_message_weapon_slots then c_message_weapon_slots(text, channel) end if c_message_exclude_dropped then c_message_exclude_dropped(text, channel) end if c_message_safe_consumables then c_message_safe_consumables(text, channel) end if c_message_search_altars_in_temple then c_message_search_altars_in_temple(text, channel) end if c_message_fully_recover then c_message_fully_recover(text, channel) end if c_message_mute_swaps then c_message_mute_swaps(text, channel) end if c_message_after_shaft then c_message_after_shaft(text, channel) end if c_message_inscribe_dps then c_message_inscribe_dps(text, channel) end end ------------------------------------------- ---------- c_assign_inv_letter() ---------- ------------------------------------------- function c_assign_invletter(it) -- Calls with no return values; just triggering on new item pickup if c_assign_invletter_item_alerts then c_assign_invletter_item_alerts(it) end if c_assign_invletter_remind_identify then c_assign_invletter_remind_identify(it) end if c_assign_invletter_exclude_dropped then c_assign_invletter_exclude_dropped(it) end if c_assign_invletter_drop_inferior then c_assign_invletter_drop_inferior(it) end if c_assign_invletter_mute_swaps then c_assign_invletter_mute_swaps(it) end -- Calls with possible return values local ret_val = nil if c_assign_invletter_weapon_slots then ret_val = c_assign_invletter_weapon_slots(it) end if ret_val then return ret_val end end --------------------------- ---- ch_stop_running() ---- --------------------------- function ch_stop_running(kind) if ch_stop_running_full_recovery then ch_stop_running_full_recovery(kind) end end --------------------------- --------- ready() --------- --------------------------- local last_turn = -1 function ready() if you.turns() == last_turn then return end last_turn = you.turns() if ready_force_mores then ready_force_mores() end if ready_dynamic_options then ready_dynamic_options() end if ready_weapon_slots then ready_weapon_slots() end if ready_safe_consumables then ready_safe_consumables() end if ready_mute_swaps then ready_mute_swaps() end if ready_ignore_altars then ready_ignore_altars() end if ready_ignore_exits then ready_ignore_exits() end if ready_after_shaft then ready_after_shaft() end if ready_fully_recover then ready_fully_recover() end end ----------------------------- ---- Prompt auto-answers ---- ----------------------------- function c_answer_prompt(prompt) if prompt:find("cheaper one?") then crawl.mpr("Replacing shopping list items", "plain") return true end if prompt == "Die?" then return false end end --Avoid weird effects after save/restart if you.turns() > 0 then ready() end }