#### Bindkeys #### # All commands and their key binds # https://github.com/jmbjr/dcss/blob/master/crawl-ref/docs/keybind.txt bindkey = [c] CMD_AUTOFIGHT_NOMOVE bindkey = [s] CMD_WEAPON_SWAP #### Interface #### tile_display_mode = tiles #tile_font_crt_size = 15 tile_font_crt_size = 18 #tile_font_stat_size = 16 tile_font_stat_size = 18 #tile_font_msg_size = 14 tile_font_msg_size = 18 # tile_font_tip_size = 15 tile_font_tip_size = 18 # tile_font_lbl_size = 14 tile_font_lbl_size = 18 game_scale = 1 tile_map_pixels = 12 tile_viewport_scale = 1.2 tile_map_scale = .8 tile_show_minihealthbar = true tile_show_minimagicbar = true tile_water_anim = true tile_misc_anim = true tile_realtime_anim = true tile_show_player_species = false tile_layout_priority = minimap, inventory, command, spell, monster tile_single_column_menus = true msg_min_height = 7 msg_max_height = 15 msg_webtiles_height = -1 tile_show_threat_levels = nasty tile_level_map_hide_messages = true tile_level_map_hide_sidebar = false tile_web_mouse_control = true # HP and MP bar coloring view_delay = 500 hp_colour = 100:lightgreen, 99:lightgray, 75:yellow, 50:lightred, 25:red mp_colour = 100:cyan, 99:lightgray, 75:yellow, 50:lightred, 25:red hp_warning = 50 # Menus sort_menus = true : equipped, identified, basename, qualname, charged ability_menu = true spell_menu = false # Monster colors #monster_list_colour = #monster_list_colour += friendly:green,neutral:brown #monster_list_colour += good_neutral:brown,strict_neutral:brown #monster_list_colour += trivial:darkgrey,easy:lightgrey #monster_list_colour += tough:yellow,nasty:lightred #### Gameplay #### default_manual_training = true autofight_stop = 60 autofight_caught = true autofight_throw = false autofight_wait = true allow_self_target = no always_show_zot = true show_game_time = true #### Items and Inventory #### equip_unequip = true ## Autopickup ## autopickup_starting_ammo = true default_autopickup = true pickup_thrown = true assign_item_slot = backward pickup_menu_limit = 1 autopickup = $?!+=/} ae := autopickup_exceptions ae += amnesia #ae += 2) or armourname:find("dragon") or armourname:find("troll") then return it.artefact else return it.artefact or it.branded or it.ego end end return true end if (sub_type == "shield") then if equipped_item then return it.artefact or it.branded or it.ego end end end end) } ------------------------- -- Dynamic Force Mores -- ------------------------- { safe = you.feel_safe() function update_safe() local old_safe = safe safe = you.feel_safe() if not safe and old_safe then crawl.mpr("Danger!", "warning") crawl.more() end end function ready() update_safe() end } { last_turn = you.turns() fm_patterns = { {name = "XL5", cond = "xl", cutoff = 5, pattern = "adder|gnoll"}, {name = "50mhp", cond = "mhp", cutoff = 50, pattern = "orc priest|electric eel|gnoll"}, {name = "60mhp", cond = "mhp", cutoff = 60, pattern = "acid dragon|steam dragon|manticore|ogre|centaur|killer bee|water moccasin"}, {name = "80mhp", cond = "mhp", cutoff = 80, pattern = "gargoyle|meliai|yaktaur|orc warrior|troll"}, {name = "90mhp", cond = "mhp", cutoff = 90, pattern = "efreet|molten gargoyle|tengu conjurer|orc (warlord|knight)"}, {name = "110mhp", cond = "mhp", cutoff = 110, pattern = {"centaur warrior|deep elf|cyclops|efreet|molten gargoyle", "tengu conjurer|yaktaur captain|necromancer|deep troll earth mage|boulder beetle|stone giant|ugly thing|two-headed ogre|ogre mage"}}, {name = "160mhp", cond = "mhp", cutoff = 160, pattern = {"(fire|ice|quicksilver|shadow|storm) dragon", "(fire|frost) giant", "war gargoyle","hydra","thorn hunter","minotaur","merfolk javelineer"}}, {name = "500mhp", cond = "mhp", cutoff = 500, pattern = "orb of fire|caustic shrike|curse skull|curse toe|iron giant|juggernaut|shining eye"}, } -- end fm_patterns active_fm = {} -- Set to true to get a message when the fm change notify_fm = false -- Wrapper of crawl.mpr() that prints text in white by default. if not mpr then mpr = function (msg, color) if not color then color = "white" end crawl.mpr("<" .. color .. ">" .. msg .. "") end end function init_force_mores() for i,v in ipairs(fm_patterns) do active_fm[#active_fm + 1] = false end end function update_force_mores() local activated = {} local deactivated = {} local hp, maxhp = you.hp() 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 msg == nil then msg = p else msg = msg .. "|" .. p end end else msg = v.pattern end msg = "(" .. msg .. ").*into view" local action = nil local fm_name = v.pattern if v.name then fm_name = v.name end if not v.cond and not active_fm[i] then action = "+" elseif v.cond == "xl" then if active_fm[i] and you.xl() >= v.cutoff then action = "-" elseif not active_fm[i] and you.xl() < v.cutoff then action = "+" end elseif v.cond == "rf" then if active_fm[i] and you.res_fire() >= v.cutoff then action = "-" elseif not active_fm[i] and you.res_fire() < v.cutoff then action = "+" end elseif v.cond == "rc" then if active_fm[i] and you.res_cold() >= v.cutoff then action = "-" elseif not active_fm[i] and you.res_cold() < v.cutoff then action = "+" end elseif v.cond == "relec" then if active_fm[i] and you.res_shock() >= v.cutoff then action = "-" elseif not active_fm[i] and you.res_shock() < v.cutoff then action = "+" end elseif v.cond == "rpois" then if active_fm[i] and you.res_poison() >= v.cutoff then action = "-" elseif not active_fm[i] and you.res_poison() < v.cutoff then action = "+" end elseif v.cond == "rcorr" then if active_fm[i] and you.res_corr() then action = "-" elseif not active_fm[i] and not you.res_corr() then action = "+" end elseif v.cond == "rn" then if active_fm[i] and you.res_draining() >= v.cutoff then action = "-" elseif not active_fm[i] and you.res_draining() < v.cutoff then action = "+" end elseif v.cond == "fly" then if active_fm[i] and not you.flying() then action = "-" elseif not active_fm[i] and you.flying() then action = "+" end elseif v.cond == "mhp" then if active_fm[i] and maxhp >= v.cutoff then action = "-" elseif not active_fm[i] and maxhp < v.cutoff then action = "+" end end if action == "+" then activated[#activated + 1] = fm_name elseif action == "-" then deactivated[#deactivated + 1] = fm_name end if action ~= nil 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 mpr("Activating force_mores: " .. table.concat(activated, ", ")) end if #deactivated > 0 and notify_fm then mpr("Deactivating force_mores: " .. table.concat(deactivated, ", ")) end end local last_turn = nil function force_mores() if last_turn ~= you.turns() then update_force_mores() last_turn = you.turns() end end init_force_mores() } ######################### # Equipement Autopickup # ######################### { local need_skills_opened = true local have_gds = false function ready() force_mores() if you.turns() == 0 and need_skills_opened then need_skills_opened = false crawl.sendkeys("m") end end local want_axe = false local want_bow = false local want_polearm = false local want_shield = true local want_body = false local function pickup_equipment(it, name) if it.is_useless then return end local class = it.class(true) local name = it.name() if class == "armour" then local good_slots = {cloak="Cloak", helmet="Helmet", gloves="Gloves", boots="Boots"} st, _ = it.subtype() if good_slots[st] ~= nil then if good_slots[st] == "Gloves" and you.has_claws() > 0 then return end if it.artefact then return true end local cur = items.equipped_at(good_slots[st]) if cur == nil then return true end if name:find("scarf") and you.xl()<20 then return true end if cur.branded or cur.artefact then return end if it.branded then return true end elseif st == "body" and want_body then local cur = items.equipped_at("armour") if cur == nil then return end if have_gds == false and name:find("gold dragon scales") then have_gds = true return true end if you.xl() > 10 then if not it.artefact and not possible_brand(name) then return end end local aValue = cur.ac * 20 if cur.plus then aValue = aValue + cur.plus * 10 end local bValue = it.ac * 20 if it.plus then bValue = bValue + it.plus * 10 end if it.artefact then bValue = bValue + 120 end if possible_brand(name) then bValue = bValue + 20 end if bValue > aValue then return true end elseif st == "shield" and want_shield then if you.base_skill("Shields") > 14 and it.ac < 5 then return end if it.artefact then return true end local cur = items.equipped_at("Shield") if cur == nil then if you.xl()<14 then return true else return end end if it.ac >= cur.ac then if it.branded or possible_brand(name) then return true end end end elseif class == "weapon" then if it.ego() == "Distortion" then return end local cur = items.equipped_at("Weapon") if cur == nil then return end local aValue = cur.damage * 10 if cur.plus then aValue = aValue + cur.plus * 5 end if cur.hands == 2 and want_shield then aValue = aValue - 40 end if cur.ego() == "vampirism" then aValue = aValue + 75 end if cur.ego() == "flaming" or cur.ego() == "freezing" or cur.ego() == "electrocution" or cur.ego() == "chopping" then aValue = aValue + 35 end if cur.ego() == "venom" or cur.ego() == "draining" or cur.ego() == "protection" then aValue = aValue + 20 end local bValue = it.damage * 10 if it.plus then bValue = bValue + it.plus * 5 end if it.ego() == "vampirism" then bValue = bValue + 50 end if it.hands == 2 and want_shield then bValue = bValue - 40 end if it.ego() == "flaming" or it.ego() == "freezing" or it.ego() == "electrocution" or it.ego() == "chopping" then bValue = bValue + 35 end if it.ego() == "venom" or it.ego() == "draining" or it.ego() == "protection" then bValue = bValue + 20 end if it.artefact and not it.fully_identified then bValue = bValue + 100 end if possible_brand(name) then bValue = bValue + 75 end if name:find("enchant") then bValue = bValue + 15 end if it.weap_skill == "Axes" and want_axe then if you.xl() < 18 then if possible_brand(name) or it.ego() == "flaming" then return true end end if bValue > aValue then return true end elseif it.weap_skill == "Polearms" and want_polearm then if bValue > aValue then return true end elseif it.weap_skill == "Bows" and want_bow then if you.xl() < 18 then if name:find("runed") or name:find("glowing") or name:find("shiny") then return true end end local bValue = it.damage * 10 if it.plus then bValue = bValue + it.plus * 3 end if it.ego() == "flaming" or it.ego() == "freezing" then bValue = bValue + 20 end if it.artefact and not it.fully_identified then bValue = bValue + 100 end if name:find("runed") or name:find("glowing") or name:find("shiny") then bValue = bValue + 50 end if bValue > aValue then return true end end end return end add_autopickup_func(pickup_equipment) function possible_brand(name) if name:find("runed") or name:find("glowing") or name:find("dyed") or name:find("embroidered") or name:find("shiny") then return true end return end } ################## # Ready Function # ################## { local need_skills_opened = true function ready() force_mores() -- Skill menu at game start by rwbarton if you.turns() == 0 and need_skills_opened then need_skills_opened = false crawl.sendkeys("m") end end } ########## # Weapon # ########## { function c_message(text, channel) if string.match(text, "!checkdmg") then local item = items.equipped_at(0) if item == nil or is_valid_weapon(item) then display_damage(item) else display_line("No valid weapon equipped...", "magenta") end end if string.match(text, "!bestwep") then compare_damage() end end function is_valid_weapon(item) return item:class() == "Hand Weapons" or item:class() == "Magical Staves" end function is_staff(item) return string.match(item:class(), "Magical Staves") ~= nil end function unarmed_data() local base_damage = 3 local claws_level = you.get_base_mutation_level("claws") local skill = you.skill("Unarmed Combat") local item = { damage = (base_damage + 2 * claws_level) + skill, ego = function() return nil end, name = function() return "Unarmed" end, name_coloured = function() return "Unarmed" end, subtype = function() return "Unarmed" end, class = function() return "Hand Weapons" end, branded = false, fully_identified = true, is_useless = false, weap_skill = "Unarmed Combat", accuracy = 0, plus = 0, delay = 10 - 1 * (skill / 5.4), is_unarmed = true, } return item end function compare_damage() display_line("Comparing items in your inventory and under your feet...") display_line("Fighting against imaginary ogres...", "darkgray") local highest_base_damage = 0 local highest_total_damage = 0 local highest_base_damage_weapon local highest_total_damage_weapon local items_ = items.inventory() local weapons = { unarmed_data() } function get_weapons_from(tab) if tab ~= nil and tab[0] == nil then for i, item in ipairs(tab) do if type(item) == "userdata" then -- display_line((item)) if is_valid_weapon(item) and item.fully_identified and not (item.is_useless) then -- weapon table.insert(weapons, item) end end end end end get_weapons_from(items.inventory()) -- display_line(tostring(items.shopping_list())) get_weapons_from(you.floor_items()) -- get_weapons_from(known_weapons) -- crawl.mpr(tostring(items.inventory())) local base_damage_map = {} local total_damage_map = {} for i, item in ipairs(weapons) do -- display_line(weapon:name()) local enemy_stats = { ac = 1, ev = 6 } enemy_stats.hit_chance = hit_chance(to_hit(item), enemy_stats.ev) local base_damage = get_dpt(item, enemy_stats, false, false) local total_damage = (item.branded or is_staff(item)) and get_dpt(item, enemy_stats, true, false) or base_damage -- display_line(tostring(base_damage) .. " " .. tostring(total_damage)) -- item.base_damage = base_damage -- item.total_damage = total_damage base_damage_map[item] = base_damage total_damage_map[item] = total_damage if base_damage >= highest_base_damage then highest_base_damage = base_damage highest_base_damage_weapon = item end if total_damage >= highest_total_damage then highest_total_damage = total_damage highest_total_damage_weapon = item end end -- display_line("Highest damage: " .. highest_base_damage_weapon:name_coloured() .. " at " .. fmt_num(highest_base_damage) .. " damage per 1.0 AUTs", "white") display_line("Highest total (branded) damage: " .. highest_total_damage_weapon:name_coloured() .. " at " .. fmt_num(base_damage_map[highest_total_damage_weapon]) .. " / " .. fmt_num(total_damage_map[highest_total_damage_weapon]) .. " damage per 1.0 AUTs", "white") crawl.more() function sort_predicate(a, b) return total_damage_map[a] > total_damage_map[b] end table.sort(weapons, sort_predicate) for i, item in ipairs(weapons) do if i > 1 then local damage = fmt_num(base_damage_map[item]) .. " / " .. fmt_num(total_damage_map[item]) display_line(tostring(i) .. ". " .. item:name_coloured() .. " - " .. damage .. " dmg") end end end function display_line(text, color) color = color or "lightgray" crawl.message("<"..color..">"..text.."", 0) end function display_damage(item) -- if item == nil then -- return -- end -- display_line(item:class()) item = item or unarmed_data() local enemy_stats = { ac = 1, ev = 6} crawl.mpr("Enemy AC (default 1): ", 2) local ac = tonumber(crawl.c_input_line()) crawl.mpr("Enemy EV (default 6): ", 2) local ev = tonumber(crawl.c_input_line()) if type(ac) == "number" then enemy_stats.ac = ac end if type(ev) == "number" then enemy_stats.ev = ev end enemy_stats.hit_chance = hit_chance(to_hit(item), enemy_stats.ev) local dpt_no_brand = get_dpt(item, enemy_stats, false) display_line("Warning: this figure is a VERY rough approximation and does not account for armor encumbrance, slaying, and most other buffs or status effects.", "lightred") display_line("Fighting against imaginary enemy with " .. tostring(enemy_stats.ac) .. " AC and " .. tostring(enemy_stats.ev) .. " EV...", "darkgray") if item.is_unarmed then display_line("Unarmed calculations assume no gloves worn.", "magenta") end function extra_reports(has_ego) -- local ego_avg_damage = has_ego and (" / " .. fmt_num(get_damage(item, enemy_stats, true))) or "" local ego_max_damage = has_ego and (" / " .. fmt_num(get_damage(item, enemy_stats, true, true))) or "" display_line("Average damage per attack: " .. fmt_num(get_damage(item, enemy_stats, false)) .. ego_avg_damage .. " ~(" .. fmt_num(weapon_delay(item) / 10) .. " AUTs)", "green") display_line("Max damage per attack: " .. fmt_num(get_damage(item, enemy_stats, false, true)) .. ego_max_damage, "green") end if not (item.branded or is_staff(item)) then display_line("Approx pre-buff weapon damage per 1.0 AUTs: " .. "" .. tostring(dpt_no_brand) .. "", "lightgreen") extra_reports(false) else local dpt_with_brand = get_dpt(item, enemy_stats, true) display_line("Approx pre-buff weapon damage per 1.0 AUTs:", "lightgreen") display_line("(before brand / after brand vs susceptible creatures)", "lightgreen") display_line(" " .. tostring(dpt_no_brand) .. " / " .. tostring(dpt_with_brand), "lightcyan") extra_reports(true) end local hit_chance = enemy_stats.hit_chance * 100 local hit_chance_color = "darkgrey" if hit_chance >= 10 then hit_chance_color = "red" end if hit_chance >= 30 then hit_chance_color = "lightred" end if hit_chance >= 50 then hit_chance_color = "yellow" end if hit_chance >= 70 then hit_chance_color = "green" end if hit_chance >= 90 then hit_chance_color = "lightgreen" end display_line("Chance to hit: <" .. hit_chance_color .. ">" .. "~" .. fmt_num(hit_chance) .. "%" .. "") -- item.description = item.description .. message_string end function get_dpt(item, enemy_stats, use_brand, format_result) if format_result == nil then format_result = true end local damage_per_swing = get_damage(item, enemy_stats, use_brand) local additional_notes = "" local you_delay = weapon_delay(item) local venom_adjustment = 0 if use_brand then if is_staff(item) then if item:subtype() == "poison" then local activation_chance = math.min((50 + you.skill("Poison Magic") * 12.5), 100) venom_adjustment = midrange(1, 7) * (activation_chance / 100) additional_notes = additional_notes .. " (estimated adjustment for poison damage, " .. fmt_num(activation_chance) .. "% chance to activate)" end else if item:ego() == "venom" then venom_adjustment = midrange(1, 7) additional_notes = additional_notes .. " (estimated adjustment for poison damage, 75% chance to activate)" end end end local result = damage_per_swing * (1 / (you_delay / 10)) + venom_adjustment if format_result then return fmt_num(result) .. additional_notes else return result end end function get_damage(item, enemy_stats, use_brand, max) local dice_avg = not max and dice_avg or (function(num_dice, num_sides) return num_dice * num_sides end) -- if we're looking for the max possible damage, dont get the average of any dice rolls - just the highest possible rolls. -- ignores slaying bonus, other "final multipliers" --[[ Damage = { [1d(Base damage * Strength modifier +1)-1] * Weapon skill modifier * Fighting modifier + Misc modifiers + Slaying bonuses } * Final multipliers + Stabbing bonus - AC damage reduction[1] --]] local die_num = item.damage * strength_modifier(max) + 1 local effective_enchantment = item.plus or 0 if effective_enchantment > 0 then effective_enchantment = dice_avg(1, 1 + effective_enchantment) - 1 elseif effective_enchantment < 0 then effective_enchantment = -dice_avg(1, 1 - effective_enchantment) + 1 end local damage = math.max(((dice_avg(1, die_num) - 1) * weapon_skill_modifier(item, max) * fighting_modifier(max)) + effective_enchantment - (enemy_stats.ac/2), 0) if not max then damage = damage * enemy_stats.hit_chance end -- display_line(tostring(string.match(item:class(), "Magical Staves"))) if use_brand then if is_staff(item) then damage = damage + staff_bonus(item, damage, max) else damage = damage + brand_bonus(item, damage, max) end end return damage end function weapon_delay(item) if item.is_unarmed then return item.delay end local base_delay = item.delay local min_delay = math.min(base_delay / 2, 7) if item.subtype() == "rapier" then min_delay = 5 end local you_delay = math.max(base_delay - math.ceil(you.skill(item.weap_skill)/2 + 0.5), min_delay) if item:ego() == "speed" then you_delay = you_delay * 0.66666 end return you_delay end function hit_chance(to_hit, ev) if ev <= 0 then return 0.99 end local total = 0 for i=1,to_hit do for j=1,ev do if i >= j then total = total + 1 end end end return total / (to_hit * ev) end function to_hit(item) local base = 15 + you.dexterity()/2 + you.skill("Fighting")/2 -- item.plus = item.plus local weapon_accuracy = you.skill(item.weap_skill)/2 + item.accuracy + (item.plus or 0) base = base + weapon_accuracy return (base-1) / 2 -- average roll end function strength_modifier(max) local dice_avg = not max and dice_avg or (function(num_dice, num_sides) return num_dice * num_sides end) local strength = you.strength() if strength > 10 then return (39 + ((dice_avg(1, strength - 8) - 1) * 2)) / 39 elseif strength < 10 then return (39 - ((dice_avg(1, 12 - strength) - 1) * 3)) / 39 else return 1 end end function weapon_skill_modifier(item, max) local dice_avg = not max and dice_avg or (function(num_dice, num_sides) return num_dice * num_sides end) local modifier = (2499 + dice_avg(1, 100 * you.skill(item.weap_skill) + 1)) / 2500 return modifier end function fighting_modifier(max) local dice_avg = not max and dice_avg or (function(num_dice, num_sides) return num_dice * num_sides end) return (3999 + dice_avg(1, 100 * you.skill("Fighting") + 1)) / 4000 end function brand_bonus(item, damage, max) local midrange = not max and midrange or (function(low, high) return high end) local dice_avg = not max and dice_avg or (function(num_dice, num_sides) return num_dice * num_sides end) local brand = item:ego() local bonuses = { disruption = damage * 0.67, draining = damage * 0.25, electrocution = midrange(8, 20) * 0.33, flaming = damage * 0.25, freezing = damage * 0.25, ["holy wrath"] = damage * 0.75, pain = damage * you.skill("Necromancy")/2, silver = damage * 0.1667, vorpal = damage * 0.1667, } local bonus = 0 for k,v in pairs(bonuses) do if string.match(brand, k) then bonus = v break end end return bonus end function staff_bonus(item, damage, max) local midrange = not max and midrange or (function(low, high) return high end) local item_type = item:subtype() function calculate_bonus(school) local evo_skill = you.skill("Evocations") local school_skill = you.skill(school) return midrange(0, 1.25 * (school_skill+evo_skill/2)) * midrange(0, 6.66 + (evo_skill + school_skill/2)) / 100 end local bonuses = { fire = calculate_bonus("Fire Magic"), cold = calculate_bonus("Ice Magic"), earth = calculate_bonus("Earth Magic"), air = calculate_bonus("Air Magic"), conjuration = calculate_bonus("Conjurations"), death = calculate_bonus("Necromancy")} -- display_line("got here") return bonuses[item_type] or 0 end function dice_avg(num_dice, num_sides) return ((num_sides + 1) / 2) * num_dice end function midrange(low, high) return (low + high) / 2 end function round_tenths(num) return round(num * 10) / 10 end function round(num) return math.floor(num + 0.5) end function random_round(num) end function fmt_num(num) return tostring(round_tenths(num)) end }