< local ATTITUDE_HOSTILE = 0 local ATTITUDE_NEUTRAL = 1 local LOS_RADIUS = 8 AUTOFIGHT_STOP = 75 AUTOFIGHT_HUNGER_STOP = 0 AUTOFIGHT_HUNGER_STOP_UNDEAD = false AUTOFIGHT_CAUGHT = false AUTOFIGHT_THROW = false AUTOFIGHT_THROW_NOMOVE = true AUTOFIGHT_FIRE_STOP = false AUTOFIGHT_WAIT = false AUTOFIGHT_PROMPT_RANGE = true AUTOMAGIC_ACTIVE = false local direction_key_map = { [-1] = { [-1] = 'y', [0] = 'h', [1] = 'b'}, [0] = { [-1] = 'k', [1] = 'j'}, [1] = { [-1] = 'u', [0] = 'l', [1] = 'n'}, } local function direction_to_key(dx, dy) return direction_key_map[dx][dy] end local function sign(a) return a > 0 and 1 or a < 0 and -1 or 0 end local function abs(a) return a * sign(a) end local function adjacent(dx, dy) return abs(dx) <= 1 and abs(dy) <= 1 end local function vector_move(dx, dy) local key_sequence = "" for i = 1, abs(dx) do key_sequence = key_sequence .. direction_to_key(sign(dx), 0) end for i = 1, abs(dy) do key_sequence = key_sequence .. direction_to_key(0, sign(dy)) end return key_sequence end local function is_safe_square(dx, dy) if view.feature_at(dx, dy) == "trap_web" then return false end return view.is_safe_square(dx, dy) end local function can_move_maybe(dx, dy) if view.feature_at(dx,dy) ~= "unseen" and view.is_safe_square(dx, dy) then local m = monster.get_monster_at(dx, dy) if not m or not m:is_firewood() then return true end end return false end local function can_move_now(dx, dy) local m = monster.get_monster_at(dx, dy) return (is_safe_square(dx, dy) and (not m or m:attitude() > ATTITUDE_NEUTRAL)) end local function try_move(mx, my, square_passability_check_function) if mx == 0 and my == 0 then return nil elseif abs(ax + mx) > LOS_RADIUS or abs(ay + my) > LOS_RADIUS then return nil elseif square_passability_check_function(ax + mx, ay + my) then return {mx, my} else return nil end end local function try_move_lower(dx, dy, square_passability_check_function, move) crawl.mpr("Moving down") if abs(dy) == 1 then move = try_move(sign(dx), 0, square_passability_check_function) end if move == nil then move = try_move(sign(dx), sign(dy), square_passability_check_function) end if move == nil then move = try_move(sign(dx), 0, square_passability_check_function) end if move == nil and abs(dx) > abs(dy) + 1 then move = try_move(sign(dx), 1, square_passability_check_function) end if move == nil and abs(dx) > abs(dy) + 1 then move = try_move(sign(dx), -1, square_passability_check_function) end if move == nil then move = try_move(0, sign(dy), square_passability_check_function) end return move end local function try_move_straight(dx, dy, square_passability_check_function, move) crawl.mpr("Moving straight") move = try_move(sign(dx), sign(dy), square_passability_check_function) if move == nil then move = try_move(sign(dx), 0, square_passability_check_function) end if move == nil then move = try_move(0, sign(dy), square_passability_check_function) end return move end local function try_move_upper(dx, dy, move, square_passability_check_function) crawl.mpr("Moving up") if abs(dx) == 1 then move = try_move(0, sign(dy), square_passability_check_function) end if move == nil then move = try_move(sign(dx), sign(dy), square_passability_check_function) end if move == nil then move = try_move(0, sign(dy), square_passability_check_function) end if move == nil and abs(dy) > abs(dx) + 1 then move = try_move(1, sign(dy), square_passability_check_function) end if move == nil and abs(dy) > abs(dx) + 1 then move = try_move(-1, sign(dy), square_passability_check_function) end if move == nil then move = try_move(sign(dx), 0, square_passability_check_function) end return move end local function choose_move_towards(ax, ay, bx, by, square_passability_check_function) local move = nil local dx = bx - ax local dy = by - ay if abs(dx) > abs(dy) then move = try_move_lower(dx, dy, move, square_passability_check_function) elseif abs(dx) == abs(dy) then move = try_move_straight(dx, dy, move, square_passability_check_function) else move = try_move_upper(dx, dy, move, square_passability_check_function) end return move end local function move_towards(dx, dy) local move = choose_move_towards(0, 0, dx, dy, can_move_now) if move == nil then crawl.mpr("Failed to move towards target.") else local key_sequence = direction_to_key(move[1], move[2]) crawl.mpr("Moving keys " .. key_sequence) crawl.process_keys(key_sequence) end end local function will_tab(ax, ay, bx, by) if abs(bx - ax) <= 1 and abs(by - ay) <= 1 or abs(bx - ax) <= 2 and abs(by - ay) <= 2 and have_reaching() then return true end local move = choose_move_towards(ax, ay, bx, by, can_move_maybe) if move == nil then return false end return will_tab(ax + move[1], ay + move[2], bx, by) end local function have_melee() local item = items.equipped_at("weapon") return item and item.class == "weapon" and not item.is_ranged and not item.is_melded end local function have_ranged() local item = items.equipped_at("weapon") return item and item.class == "weapon" and item.is_ranged and not item.is_melded end local function have_reaching() local item = items.equipped_at("weapon") return item and item.class == "weapon" and item.reach_range == 8 and not item.is_melded end local function have_throwing(no_move) return (AUTOFIGHT_THROW or no_move and AUTOFIGHT_THROW_NOMOVE) and items.fired_item() ~= nil end local function get_monster_info(dx, dy, no_move) mon = monster.get_monster_at(dx, dy) if not mon then return nil end name = mon:name() info = {} info.distance = (abs(dx) > abs(dy)) and -abs(dx) or -abs(dy) if have_ranged() then info.attack_type = you.see_cell_no_trans(dx, dy) and 3 or 0 elseif not have_reaching() then info.attack_type = (-info.distance < 2) and 2 or 0 else if -info.distance > 2 then info.attack_type = 0 elseif -info.distance < 2 then info.attack_type = 2 else info.attack_type = view.can_reach(dx, dy) and 1 or 0 end end if info.attack_type == 0 and have_throwing(no_move) and you.see_cell_no_trans(dx, dy) then info.attack_type = 3 end if info.attack_type == 0 and not will_tab(0, 0, dx, dy) then info.attack_type = -1 end info.can_attack = (info.attack_type > 0) and 1 or info.attack_type info.safe = mon:is_safe() and -1 or 0 info.constricting_you = mon:is_constricting_you() and 1 or 0 info.very_stabbable = (mon:stabbability() >= 1) and 1 or 0 info.injury = mon:damage_level() info.threat = mon:threat() return info end local function compare_monster_info(m1, m2) flag_order = autofight_flag_order if flag_order == nil then flag_order = {"can_attack", "safe", "distance", "constricting_you", "very_stabbable", "injury", "threat"} end for i, flag in ipairs(flag_order) do if m1[flag] > m2[flag] then return true elseif m1[flag] < m2[flag] then return false end end return false end local function is_candidate_for_attack(x, y) mon = monster.get_monster_at(x, y) if not mon or mon:attitude() ~= ATTITUDE_HOSTILE then return false end if mon:name() == "butterfly" or mon:name() == "orb of destruction" then return false end if mon:is_firewood() then if string.find(mon:name(), "ballistomycete") then return true else return false end end return true end local function get_target(no_move) local x, y, new_info local best_x = 0 local best_y = 0 local best_info = nil for x = -LOS_RADIUS, LOS_RADIUS do for y = -LOS_RADIUS, LOS_RADIUS do if is_candidate_for_attack(x, y) then new_info = get_monster_info(x, y, no_move) if (not best_info) or compare_monster_info(new_info, best_info) then best_x = x best_y = y best_info = new_info end end end end return best_x, best_y, best_info end local function execute_action_key_sequence(action_begin_keys, action_positioning_keys, action_end_keys) local key_sequence = "" for index, value in pairs(action_begin_keys) do key_sequence = key_sequence .. value end for index, value in pairs(action_positioning_keys) do key_sequence = key_sequence .. value end for index, value in pairs(action_end_keys) do key_sequence = key_sequence .. value end crawl.process_keys(key_sequence) end local function attack_fire(x, y) local action_begin_keys = {'f', 'r'} local action_positioning_keys = vector_move(x, y) local action_end_keys = {'f'} execute_action_key_sequence(action_begin_keys, action_positioning_keys, action_end_keys) end local function attack_fire_stop(x, y) local action_begin_keys = {'f', 'r'} local action_positioning_keys = vector_move(x, y) local action_end_keys = {'.'} execute_action_key_sequence(action_begin_keys, action_positioning_keys, action_end_keys) end local function attack_reach(x, y) local action_begin_keys = {'v', 'r'} local action_positioning_keys = vector_move(x, y) local action_end_keys = {'.'} execute_action_key_sequence(action_begin_keys, action_positioning_keys, action_end_keys) end local function attack_melee(x, y) local key_sequence = direction_to_key(x, y) crawl.process_keys(key_sequence) end local function set_stop_level(key, value, mode) AUTOFIGHT_STOP = tonumber(value) end local function set_hunger_stop_level(key, value, mode) AUTOFIGHT_HUNGER_STOP = tonumber(value) end local function set_hunger_stop_undead(key, value, mode) AUTOFIGHT_HUNGER_STOP_UNDEAD = string.lower(value) ~= "false" end local function set_af_caught(key, value, mode) AUTOFIGHT_CAUGHT = string.lower(value) ~= "false" end local function set_af_throw(key, value, mode) AUTOFIGHT_THROW = string.lower(value) ~= "false" end local function set_af_throw_nomove(key, value, mode) AUTOFIGHT_THROW_NOMOVE = string.lower(value) ~= "false" end local function set_af_fire_stop(key, value, mode) AUTOFIGHT_FIRE_STOP = string.lower(value) ~= "false" end local function set_af_wait(key, value, mode) AUTOFIGHT_WAIT = string.lower(value) ~= "false" end local function set_af_prompt_range(key, value, mode) AUTOFIGHT_PROMPT_RANGE = string.lower(value) ~= "false" end function af_hp_is_low() local hp, mhp = you.hp() return (100 * hp <= AUTOFIGHT_STOP * mhp) end function af_food_is_low() if you.race() == "Mummy" or you.transform() == "lich" then return false elseif (not AUTOFIGHT_HUNGER_STOP_UNDEAD) and (you.race() == "Vampire" or you.race() == "Ghoul") then return false else return (AUTOFIGHT_HUNGER_STOP ~= nil and you.hunger() <= AUTOFIGHT_HUNGER_STOP) end end local function weapon_check() return have_melee() end local function attack(allow_movement) local x, y, info = get_target(not allow_movement) local caught = you.caught() crawl.mpr("wtf") crawl.mpr("Attack type " .. info.attack_type) end function hit_closest() crawl.mpr("hit_closest act") attack(true) crawl.mpr("hit_closest fin") end function hit_adjacent() crawl.mpr("hit_adjacent act") attack(false) crawl.mpr("hit_adjacent fin") end function toggle_autothrow() AUTOFIGHT_THROW = not AUTOFIGHT_THROW crawl.mpr(AUTOFIGHT_THROW and "Enabling autothrow." or "Disabling autothrow.") end chk_lua_option.autofight_stop = set_stop_level chk_lua_option.autofight_hunger_stop = set_hunger_stop_level chk_lua_option.autofight_hunger_stop_undead = set_hunger_stop_undead chk_lua_option.autofight_caught = set_af_caught chk_lua_option.autofight_throw = set_af_throw chk_lua_option.autofight_throw_nomove = set_af_throw_nomove chk_lua_option.autofight_fire_stop = set_af_fire_stop chk_lua_option.autofight_wait = set_af_wait chk_lua_option.autofight_prompt_range = set_af_prompt_range > default_manual_training = true : if you.race() == "Mummy" then autopickup = ()$?!+"/%\}0 : else autopickup = $?!+"/% include = autopickup_exceptions.txt : end lua_file = lua/stash.lua lua_file = lua/wield.lua lua_file = lua/runrest.lua lua_file = lua/gearset.lua lua_file = lua/trapwalk.lua drop_filter = useless_item default_target = true darken_beyond_range = true level_map_title = true travel_delay = -1 explore_delay = -1 travel_avoid_terrain = shallow water explore_greedy = true explore_wall_bias = 1 travel_key_stop = true auto_exclude = oklob,statue,curse skull,roxanne,hyperactive,lightning spire runrest_ignore_poison = 2:30 runrest_ignore_monster = butterfly:1 include = runrest_messages.txt trapwalk_safe_hp = dart:20,needle:15,arrow:35,bolt:45,spear:40,axe:45,blade:95 annotate_item_class = true annotate_item_dropped = true sort_menus = inv: true : equipped, freshness, charged autofight_stop = 75 hp_warning = 50 mp_warning = 0 hp_colour = 75:yellow, 50:red mp_colour = 50:yellow, 25:red show_more = true item_stack_summary_minimum = 10 include = standard_colours.txt include = food_colouring.txt include = menu_colours.txt menu_colour = pickup:green:god gift menu_colour = inventory:white:\w \+\s menu_colour = inventory:white:\w \#\s include = messages.txt menu_colour = notes:white:Reached XP level force_more_message = There are no visible monsters within range force_more_message = This wand has no charges force_more_message = You have reached level force_more_message = Your scales start force_more_message = You fall through a shaft force_more_message = Careful! force_more_message = interdimensional caravan force_more_message = distant snort include = tiles_options.txt dump_item_origins = all ood_interesting = 8 note_hp_percent = 5 note_all_skill_levels = true note_all_spells = true note_xom_effects = true note_items = rod of, acquirement, of Zot note_messages = You pass through the gate note_messages = cast .* Abyss note_messages = Your scales start note_messages = protects you from harm note_messages = You fall through a shaft note_monsters = orb of fire, ancient lich, fiend