include += HDAColors.rc include += HDamage.rc include += HDAForceMore.rc { reminded_mindelay = {} function ready() crawl.enable_more(true) UpdateKnowledge() newmessages = crawl.messages(50) if newmessages:find("finish memorising") or newmessages:find("unravels") then known_spells = init_spells() end known_abils = init_abils() if you.skill(weapon_skill) >= weapon_target_skill and reminded_mindelay[weapon_type] ~= true and you.train_skill(weapon_skill) == 1 then crawl.mpr("Your wielded weapon is at mindelay!") crawl.more() sendkeys('m') reminded_mindelay[weapon_type] = true end check_safe_invisible(newmessages) safe_from_oos = true --check_safe_oos(newmessages) AnnounceDamage() if hp <= max_hp * 0.25 and not warning_25 and not waiting_to_autoexplore then crawl.mpr(string.format("HP at %i%%!",hp / max_hp * 100)) crawl.more() warning_25 = true warning_50 = true elseif hp <= max_hp * 0.5 and not warning_50 and not waiting_to_autoexplore then crawl.mpr(string.format("HP at %i%%!",hp / max_hp * 100)) crawl.more() warning_50 = true end if hp > max_hp * 0.25 then warning_25 = false end if hp > max_hp * 0.5 then warning_50 = false end local casted_permanent_spells = false if options.autopick_on and you_safe then Autobutcher() casted_permanent_spells = PermanentSpells() end OpenSkills() if not casted_permanent_spells then FinishResting() end if pillardancing and next_move == nil then Pillardance() end end function permanent_vars() vars = "" vars = vars .. stringify_var("safe_from_invisible",safe_from_invisible) vars = vars .. stringify_var("desired_thirst",desired_thirst) return vars end table.insert(chk_lua_save, permanent_vars) --############# --# Debugging # --############# function DebugOutput() debug("you_turns") debug("you.status()") debug('you_status') debug("you.god()") debug("you_are_safe()") debug("safe_from_invisible") debug("safe_from_oos") debug("options.autopick_on") debug("autopickup_off_because") debug("stringify_array(missiles_have)") debug("stringify_array(equipped_at)") debug("have_chunks") debug("you_like_chunks") debug("you_hunger") debug("you_rot") debug("you_should_rest") debug("stringify_array(you_debuffs)") debug("you_debuffed") debug("stringify_array(you_buffs)") debug("you_buffed") debug("see_corpses()") debug("over_corpses()") debug("see_skeletons()") debug("over_skeletons()") debug("stringify_array(you_spells)") debug("stringify_array(known_spells)") debug("stringify_array(known_abils)") debug("weapon_type") debug("weapon_target_skill") debug("weapon_skill") end --###################### --# Calculate Artprops # --###################### function CalculateArtprops() local attr = { Str = 0, Dex = 0, Int = 0, HP = 0, MP = 0, Slay = 0, EV = 0, AC = 0 } local pluses = { rF = 0, rC = 0, Stlth = 0, rN = 0, MR = 0 } local actions = { Blink = 0, Inv = 0, Fly = 0 } local intr = { rMut = 0, rPois = 0, rElec = 0, SInv = 0, rCorr = 0, Regen = 0, Clar = 0, Ward = 0, Spirit = 0 } local ignore = { Contam = 0, channel = 0 } for it in inventory() do if it.equipped then local name = it.name() if name:find("{") then if it.class(true) == "weapon" then name = name:gsub(".*,","") name = name:gsub("}.*","") else name = name:gsub(".*{","") name = name:gsub("[},].*","") end for prop in name:gmatch("%S+") do propname = prop:gsub("%A","") if attr[propname] ~= nil then if prop ~= "+MP" then local count = tonumber(prop:gsub("%D",""),10) if prop:find("%+") then attr[propname] = attr[propname] + count elseif prop:find("%-") then attr[propname] = attr[propname] - count end end elseif pluses[propname] ~= nil then local count = prop:gsub("%a","") if count:find("%+") then count = count:len() elseif count:find("%-") then count = count:len() *-1 end pluses[propname] = pluses[propname] + count elseif actions[propname] ~= nil then actions[propname] = actions[propname] + 1 elseif intr[propname] ~= nil then intr[propname] = intr[propname] + 1 elseif ignore[propname] == nil then crawl.mpr(string.format("What is %s?",prop)) end end end end end local arrs = { ["Attributes: "] = attr, ["Pluses: "] = pluses, ["Actions: "] = actions, ["Intrinsics: "] = intr } for name,arr in pairs(arrs) do local outstr = name for stat,val in pairs(arr) do outstr = outstr .. stat .. ": " .. tostring(val) .. ", " end crawl.mpr(string.format("%s",outstr:gsub(", $",""))) end end --###################################### --# Inventory Knowledge for Autothings # --###################################### have_knowledge = false autopickup_slots = {cloak=true, helmet=true, gloves=true, boots=true} equipment_slots = {cloak="Cloak", helmet="Helmet", gloves="Gloves", boots="Boots", body="Armour", shield="Shield", weapon="Weapon"} itemcount = 0 have_chunks = false have_blood = false equipped_at = {cloak=nil, helmet=nil, gloves=nil, boots=nil, armour=nil, shield=nil, weapon=nil} missiles_have = {} have_poison_resistance = false you_race = you.race() you_like_chunks = you_race ~= "Mummy" and you_race ~= "Spriggan" and you_race ~= "Vampire" you_like_blood = you_race == "Vampire" buffs = { ["lich"]=true, ["regenerating"]=true, ["deflect missiles"]=true, ["repel missiles"]=true, ["delayed fireball"]=true, ["bat-form"]=true } debuffs = { ["vulnerable"]=true, ["confused"]=true, ["corona"]=true, ["exhausted"]=true, ["petrifying"]=true, ["silence"]=true, ["marked"]=true, ["barbs"]=true, ["corroded"]=true, ["slowed"]=true, ["corroded equipment"]=true } function UpdateKnowledge() hp,max_hp = you.hp() mp,max_mp = you.mp() you_time = you.time() you_turns = you.turns() you_god = you.god() you_hunger = you.hunger() you_starving = (you_hunger == 0 and you_race ~= "Vampire") you_gourmand = you_are_gourmand() you_poisoned = you.poisoned() you_berserk = you.berserk() you_rot = you.rot() have_poison_resistance = you.res_poison() you_status = {} you_debuffs = {} you_buffs = {} you_debuffed = false you_buffed = false for status in string.gmatch(you.status(), "[^,]+") do you_status[status] = true if debuffs[status] then you_debuffs[status] = true you_debuffed = true elseif buffs[status] then you_buffs[status] = true you_buffed = true end end you_safe = you_are_safe() you_can_cast = can_cast_spells() you_spells = {} if you_can_cast then for spell,_ in pairs(known_spells) do you_spells[spell] = can_cast(spell) and spells.level(spell) or false end end if you_like_chunks then have_chunks = have_edible_chunks() elseif you_like_blood then have_blood = have_blood_potions() end local oldweapon = equipped_at["weapon"] for name,slot in pairs(equipment_slots) do equipped_at[name] = items.equipped_at(slot) end if equipped_at["weapon"] ~= oldweapon or weapon_target_skill == 0 then check_new_weapon() end itemcount = 0 missiles_have = {["bolt"]={},["arrow"]={},["sling bullet"]={},["needle"]={},["stone"]={},["large rock"]={},["throwing net"]={},["tomahawk"]={},["javelin"]={}} for it in inventory() do if it.class(true) == "missile" then missiles_have[it.subtype(true)][tostring(it.ego(true))] = true end itemcount = itemcount + 1 end you_should_rest = should_rest() have_knowledge = true end --#################### --# Custom Autpickup # --#################### local strfind = string.find add_autopickup_func(function(it, name) if not have_knowledge then return nil end local class = it.class(true) if class == "missile" then return missiles_have[it.subtype(true)][tostring(it.ego(true))] ~= nil end if class == "food" then if it.is_useless then return false end if strfind(name,"chunk") then if itemcount < 52 or have_chunks then return true else return false end else return nil end end if class == "armour" then if it.is_useless then return false end local sub_type = it.subtype() if autopickup_slots[sub_type] then if not equipped_at[sub_type] then return true else return (it.artefact or it.branded or it.ego) end end end end) --############### --# Autobutcher # --############### look_again = false last_butchered = nil function Autobutcher() if not you_like_chunks and not you_like_blood then return false end local oec = over_edible_corpse() local oeh = over_edible_chunk() local cec = can_eat_chunks() local wnc = want_new_chunks_or_blood() if you_like_chunks and cec and ( oeh or have_chunks ) then sendkeys("e") look_again = true elseif you_like_blood and cec and have_blood then sendkeys("q1") look_again = true elseif oec and ( cec or ( itemcount < 52 and wnc ) ) then sendkeys("c") last_butchered = you_time look_again = true elseif look_again then local itemlist = "" for it in floor_items() do itemlist = itemlist .. it.name_coloured() .. ", " end if itemlist ~= "" then crawl.mpr(string.format("Items here: %s",itemlist:sub(1,-3))) end look_again = false end end function want_new_chunks_or_blood() if you_like_chunks then return not have_chunks or last_butchered==nil or you_time >= last_butchered + 1000 elseif you_like_blood then return not have_blood or last_butchered==nil or you_time >= last_butchered + 2000 end end function can_eat_chunks() if you_like_blood then return you_hunger < desired_thirst and not you_buffs["bat-form"] end if you_race=="Ghoul" then return you_hunger<4 or you_rot>0 or hp 0 then desired_thirst = desired_thirst - 1 end crawl.mpr(string.format("Desired thirst level set to %s",thirst_levels[desired_thirst])) end function DecreaseDesiredThirst() if desired_thirst < 6 then desired_thirst = desired_thirst + 1 end crawl.mpr(string.format("Desired thirst level set to %s",thirst_levels[desired_thirst])) end --############################# --# Autocast permanent spells # --############################# function can_cast_spells() return you_safe and not you_berserk and not you_debuffs["silence"] and not you_debuffs["confused"] and not you_starving end function can_cast(spell) if you_can_cast and known_spells[spell] and spells.fail_severity(spell) < 2 and mp >= spells.level(spell) and hp >= max_hp * 0.5 then return true else return false end end function PermanentSpells() if you_can_cast then if not you_buffs["deflect missiles"] and you_spells["Deflect Missiles"] then cast("Deflect Missiles") return true elseif not you_buffs["deflect missiles"] and not you_buffs["repel missiles"] and you_spells["Repel Missiles"] then cast("Repel Missiles") return true end if not you_buffs["delayed fireball"] and you_spells["Delayed Fireball"] then cast("Delayed Fireball") return true end end return false end --############################## --# Rest up before autoexplore # --############################## function FinishRestingThenAutoexplore() waiting_to_autoexplore = true but_dont_autoexplore = false end function TriggerFinishResting() waiting_to_autoexplore = true but_dont_autoexplore = true end waiting_to_autoexplore = false but_dont_autoexplore = false time_start_wait = 0 autoswitched_to_staff_of_energy = false tried_spells = {} autospent_mp = 0 function over_corpses() it = is_at(0,0,{"corpse"}) if it ~= false and it.can_zombify then return true else return false end end function see_remains() local x,y for x=-losrange,losrange do for y=-losrange,losrange do it = is_at(x,y,{"corpse","skeleton"}) if it ~= false then if it.can_zombify or it.has_skeleton then return true end end end end return false end function try_spell(what) cast(what) autospent_mp = autospent_mp + you_spells[what] tried_spells[what] = true return true end function reset_resting() --p('reset resting') tried_spells = {} you_see_corpses = nil you_on_corpses = nil waiting_to_autoexplore = false time_start_wait = 0 autospent_mp = 0 end function FinishResting() if waiting_to_autoexplore and not PermanentSpells() then if time_start_wait == 0 then time_start_wait = you_time end if you_safe and not you_starving then crawl.enable_more(false) if you_can_cast then if you_spells["Simulacrum"] and not tried_spells["Simulacrum"] then if you_on_corpses == nil then you_on_corpses = over_corpses() end if you_on_corpses and try_spell("Simulacrum") then you_on_corpses = false you_see_corpses = see_remains() return true end end if you_spells["Animate Dead"] or known_abils["Animate Dead"] then if you_see_corpses == nil then you_see_corpses = see_remains() end if you_see_corpses and not tried_spells["Animate Dead"] and known_abils["Animate Dead"] then abil("Animate Dead") tried_spells["Animate Dead"] = true you_see_corpses = false return true elseif you_see_corpses and not tried_spells["Animate Dead"] then if try_spell("Animate Dead") then you_see_corpses = false return true end end end if not you_buffs["lich"] and you_spells["Sublimation of Blood"] and mp < max_mp and ((max_mp-mp)*3 <= 0.5*hp) then cast("Sublimation of Blood") return true elseif not you_buffs["lich"] and you_spells["Regeneration"] and hp < max_hp and not you_buffs["regenerating"] then cast("Regeneration") return true end end if is_equipped("weapon","staff of energy") and mp < max_mp and (have_chunks or not you_like_chunks) then sendkeys("v") elseif is_alternative_weapon("staff of energy") and mp < max_mp and (have_chunks or not you_like_chunks) then sendkeys("'") autoswitched_to_staff_of_energy = true elseif (you_should_rest and you_god ~= "Jiyva") or (you_should_rest and you_god == "Jiyva" and but_dont_autoexplore) then rest() else if you_time - time_start_wait > 10 then crawl.mpr(string.format("Total turns waited: %s",(you_time - time_start_wait)/10)) elseif but_dont_autoexplore then crawl.mpr("No need to wait!") end reset_resting() if not is_equipped("weapon","staff of energy") then autoswitched_to_staff_of_energy = false end if autoswitched_to_staff_of_energy then sendkeys("'") autoswitched_to_staff_of_energy = false return true end if not but_dont_autoexplore then crawl.enable_more(true) autoexplore() else but_dont_autoexplore = false end end else if you_time - time_start_wait > 10 then crawl.mpr(string.format("Total turns waited: %s",(you_time - time_start_wait)/10)) else reason = you_are_safe_reason() if reason == "unsafe" then crawl.mpr("There are dangers nearby!") elseif reason == "oos" then crawl.mpr("Monsters outside your LoS are attacking your minions!") elseif reason == "invis" then crawl.mpr("There are invisible monsters nearby!") elseif reason == "pois" then crawl.mpr("The poison in your blood would kill you!") elseif reason == "pillar" then crawl.mpr("You are pillardancing!") elseif you_starving then crawl.mpr("Starvation!") elseif you_berserk then crawl.mpr("You are berserking!") end end reset_resting() end end end --################# --# Pillardancing # --################# losrange = 7 fielddim = losrange*2 pillar_fov = {} pillar_fov_clear = {} pillardancing = false dance_direction = nil dance_direction_num = nil next_move = nil pillar_center = {nil,nil} walk_path = {[-1]={},[1]={}} order_diag = { {-1,-1}, {0,-1}, {1,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} } order_adja = { {-1,0}, {0,1}, {1,0}, {0,-1} } order_tendril = { {1,0}, {0,1}, {1,1}, {1,-1} } moves_diag = { "y", "h", "b", "j", "n", "l", "u", "k" } pseudomonsters = { ["fungus"]=true, ["plant"]=true } function rotate_forward(what) table.insert(what,#what,table.remove(what,1)) end function rotate_backward(what) table.insert(what,1,table.remove(what,#what)) end function contains(t,what) for _,f in pairs(t) do if f ~= nil and f == what then return true end end return false end function pv(v) return type(v) == "table" and "("..tostring(v[1])..","..tostring(v[2])..")" or tostring(v) end function vector_out_of_bounds(v,range) return math.abs(v[1]) > range or math.abs(v[2]) > range end function tile_at_vector(v) return pillar_fov[v[1]][v[2]] end function set_tile_at_vector(v,t) pillar_fov[v[1]][v[2]] = t end function feature_at_vector(v) return view.feature_at(v[2],v[1]) end function monster_at_vector(v) return monster.get_monster_at(v[2],v[1]) end function exclude_at_vector(v) travel.set_exclude(v[2],v[1],0) end function unexclude_at_vector(v) travel.del_exclude(v[2],v[1]) end function vector_field_iterator_from_table(t) return coroutine.wrap(function () for _,v in pairs(t) do coroutine.yield(v) end end) end function vector_field_iterator_from_box(ybound,xbound) return coroutine.wrap(function () for y=ybound[1],ybound[2] do for x=xbound[1],xbound[2] do coroutine.yield({y,x}) end end end) end function los_vectorfield() return vector_field_iterator_from_box({-losrange,losrange},{-losrange,losrange}) end function field_vectorfield() return vector_field_iterator_from_box({-fielddim,fielddim},{-fielddim,fielddim}) end function vector_field_offset(vector_field_iterator,offset) return coroutine.wrap(function () for v in vector_field_iterator do coroutine.yield(vector_add(v,offset)) end end) end function vector_equal(v1,v2) return v1[1] == v2[1] and v1[2] == v2[2] end function vector_copy(v) return {v[1],v[2]} end function vector_field_copy(vf,size) vfn = {} for y=-size,size do for x=-size,size do if vfn[y] == nil then vfn[y] = {} end vfn[y][x] = vf[y][x] end end return vfn end function vector_neg(v) return {-v[1],-v[2]} end function vector_add(v1,v2) return {v1[1]+v2[1],v1[2]+v2[2]} end function vector_sub(v1,v2) return {v1[1]-v2[1],v1[2]-v2[2]} end function vector_redir(v,dir) return {v[1]*dir[1],v[2]*dir[2]} end function vector_cross(v1,v2) return v1[1] * v2[2] - v1[2] * v2[1] end function vector_abs(v) return math.sqrt(v[1]^2+v[2]^2) end function vector_coordabs(v) return {math.abs(v[1]),math.abs(v[2])} end function vector_angle(v1,v2) return math.atan(vector_cross(v1,v2) / (vector_abs(v1) * vector_abs(v2))) end function all_fields_in(vector_field_iterator,checklist) return coroutine.wrap(function () for v in vector_field_iterator do if checklist[tile_at_vector(v)] ~= nil then coroutine.yield(v) end end end) end function not_fields_in(vector_field_iterator,checklist) return coroutine.wrap(function () for v in vector_field_iterator do if checklist[tile_at_vector(v)] == nil then coroutine.yield(v) end end end) end function get_dance_direction(move,pillar_offset) if pillar_offset == nil then pillar_offset = {0,0} end rawdir = vector_cross(vector_sub(pillar_center,pillar_offset),move) return rawdir ~= 0 and rawdir / math.abs(rawdir) or 0 end function init_fov() pillar_fov = {} for v in field_vectorfield() do if pillar_fov[v[1]] == nil then pillar_fov[v[1]] = {} end set_tile_at_vector(v,"0") if not vector_out_of_bounds(v,losrange) then f = feature_at_vector(v) mon = monster_at_vector(v) if f == nil or not travel.feature_traversable(f) or (mon and pseudomonsters[mon:name()] == true) then set_tile_at_vector(v,"1") end end end for v in vector_field_iterator_from_box({-losrange,-losrange},{-losrange,losrange}) do if tile_at_vector(v) == "1" then unfill(v) end end for v in vector_field_iterator_from_box({-losrange,losrange},{-losrange,-losrange}) do if tile_at_vector(v) == "1" then unfill(v) end end for v in vector_field_iterator_from_box({losrange,losrange},{-losrange,losrange}) do if tile_at_vector(v) == "1" then unfill(v) end end for v in vector_field_iterator_from_box({-losrange,losrange},{losrange,losrange}) do if tile_at_vector(v) == "1" then unfill(v) end end pillar_fov_clear = vector_field_copy(pillar_fov,fielddim) set_tile_at_vector({0,0},"x") end function shift_fov(next_move) set_tile_at_vector({0,0},"p") new_fov = {} for v in field_vectorfield() do vn = vector_sub(v,next_move) if not vector_out_of_bounds(vn,fielddim) then if new_fov[vn[1]] == nil then new_fov[vn[1]] = {} end new_fov[vn[1]][vn[2]] = tile_at_vector(v) else vnn = {next_move[1]*fielddim,next_move[2]*fielddim} if new_fov[vnn[1]] == nil then new_fov[vnn[1]] = {} end new_fov[vnn[1]][vnn[2]] = tile_at_vector(v) end end pillar_fov = new_fov set_tile_at_vector({0,0},"x") end function unfill(v) set_tile_at_vector(v,"2") for _,move in pairs(order_adja) do vn = vector_add(v,move) if not vector_out_of_bounds(vn,losrange) and tile_at_vector(vn) == "1" then unfill(vn) end end end function floodfill(v) set_tile_at_vector(v,"P") for _,move in pairs(order_adja) do vn = vector_add(v,move) if tile_at_vector(vn) == "1" then floodfill(vn) end end return true end function path_recursion(step,origin,path) neworigin = vector_add(origin,step) for vn in all_fields_in(vector_field_offset(vector_field_iterator_from_table(order_diag),neworigin),{["p"]=true,["x"]=true}) do vn = vector_sub(vn,neworigin) if not vector_equal(vn,vector_neg(step)) then continue = vector_copy(vn) dir = get_dance_direction(continue,neworigin) continue[3] = dir end end path[#path+1] = continue if tile_at_vector(vector_add(neworigin,continue)) == "x" then return path else return path_recursion(continue,neworigin,path) end end function find_dir_of_path(step) walk_path = {[-1]={},[1]={}} path = path_recursion(step,{0,0},{step}) dirsum = 0 for _,step in pairs(path) do dirsum = dirsum + step[3] end return dirsum / math.abs(dirsum), path end function draw_path() for v in all_fields_in(los_vectorfield(),{["P"]=true}) do for vn in not_fields_in(vector_field_offset(vector_field_iterator_from_table(order_adja),v),{["P"]=true}) do set_tile_at_vector(vn,"p") end end set_tile_at_vector({0,0},"x") end function calculate_path() possible_moves = {} for v in all_fields_in(vector_field_iterator_from_table(order_diag),{["p"]=true}) do dir = get_dance_direction(v) possible_moves[#possible_moves+1] = {v[1],v[2],dir} end d1, p1 = find_dir_of_path(possible_moves[1]) d2, p2 = find_dir_of_path(possible_moves[2]) if d1 == d2 then crawl.mpr("Couldn't determine path directions, this shouldn't happen!") StopPillardancing() else for _,v in pairs(p1) do walk_path[d1][#walk_path[d1]+1] = {v[1],v[2],d1} end for _,v in pairs(p2) do walk_path[d2][#walk_path[d2]+1] = {v[1],v[2],d2} end end end function fill_pillar() for v in all_fields_in(los_vectorfield(),{["p"]=true}) do surrounded = true for _ in not_fields_in(vector_field_offset(vector_field_iterator_from_table(order_adja),v),{["p"]=true,["P"]=true,["x"]=true,["!"]=true}) do surrounded = false break end if surrounded then set_tile_at_vector(v,"P") end end end function tendril(v,dv) for d=1,losrange do vn = vector_add(v,vector_redir({d,d},dv)) if vector_out_of_bounds(vn,losrange-1) then break end tile = tile_at_vector(vn) if (tile ~= "0" and tile ~= "p") or (tile == "p" and d == 1) then break end if tile == "p" then for dn=1,d do set_tile_at_vector(vector_add(v,vector_redir({dn,dn},dv)),"p") return true end end end end function next_to_pillar() for v in all_fields_in(vector_field_iterator_from_table(order_diag),{["1"]=true}) do if floodfill(v) then return v end pillar_fov = vector_field_copy(pillar_fov_clear,fielddim) end return nil end function monsters_at(move) ybound = {-losrange,losrange} xbound = {-losrange,losrange} if move[1] < 0 then ybound[2] = -1 elseif move[1] > 0 then ybound[1] = 1 end if move[2] < 0 then xbound[2] = -1 elseif move[2] > 0 then xbound[1] = 1 end for v in all_fields_in(vector_field_iterator_from_box(ybound,xbound),{["0"]=true,["p"]=true}) do mon = monster_at_vector(v) --travel.set_exclude(x,y,0) if mon and not mon:is_safe() then return true end end return false end function calculate_next_pillar_step() if dance_direction_num ~= nil then possible_moves = {walk_path[dance_direction_num][1],walk_path[dance_direction_num*-1][1]} else possible_moves = {walk_path[-1][1],walk_path[1][1]} end goodmove = nil for _,move in pairs(possible_moves) do goodmove = move if monsters_at(move) then if dance_direction_num ~= nil and move[3] == dance_direction_num then dance_direction_num = dance_direction_num*-1 end goodmove = nil else break end end if goodmove ~= nil then travel.del_exclude(goodmove[2], goodmove[1]) return goodmove else crawl.mpr("No suitable move found!") StopPillardancing() return nil end end function next_pillar_step() for i,c in pairs(order_diag) do if vector_equal(c,next_move) then crawl.sendkeys(moves_diag[i]) break end end pillar_center = vector_sub(pillar_center,next_move) shift_fov(next_move) rotate_forward(walk_path[next_move[3]]) rotate_backward(walk_path[next_move[3]*-1]) next_move = nil end function Pillardance() if not pillardancing then pillardancing = true init_fov() pillar_pos = next_to_pillar() if pillar_pos == nil then crawl.mpr("No pillar found!") pillardancing = false return false end draw_path() fill_pillar() for v in all_fields_in(los_vectorfield(),{["p"]=true,["x"]=true}) do for _,dv in pairs(order_tendril) do tendril(v,dv) end end fill_pillar() for v in all_fields_in(los_vectorfield(),{["p"]=true,["x"]=true}) do exclude_at_vector(v) end pillar_center = {0, 0} vectorcount = 0 --for y=-losrange,losrange do for x=-losrange,losrange do if pillar_fov[y][x] == "P" then -- pillar_center = {pillar_center[1] + y, pillar_center[2] + x} -- vectorcount = vectorcount + 1 --end end end for v in all_fields_in(los_vectorfield(),{["P"]=true}) do pillar_center = {pillar_center[1] + v[1], pillar_center[2] + v[2]} vectorcount = vectorcount + 1 end pillar_center = {pillar_center[1] / vectorcount, pillar_center[2] / vectorcount} calculate_path() next_move = calculate_next_pillar_step() if next_move == nil then return false end dance_direction_num = get_dance_direction(goodmove) dance_direction = dance_direction_num > 0 and "clockwise" or "counterclockwise" crawl.mpr("Dancing "..dance_direction.."!") else if next_move ~= nil then next_pillar_step() else next_move = calculate_next_pillar_step() exclude_at_vector({0,0}) crawl.redraw_screen() end end --for y=-fielddim,fielddim do -- ls = "" -- for x=-fielddim,fielddim do -- ls = ls .. pillar_fov[y][x] -- end -- dmsg(pad(y,3), ls) --end end function StopPillardancing() if pillardancing then for v in all_fields_in(field_vectorfield(),{["p"]=true,["x"]=true}) do unexclude_at_vector(v) end pillardancing = false dance_direction = nil dance_direction_num = nil next_move = nil pillar_center = {nil,nil} walk_path = {[-1]={},[1]={}} pillar_fov = {} pillar_fov_clear = {} end end --################################## --# Auxiliary Function Definitions # --################################## function p(what) crawl.mpr(tostring(what)) end function pad(str, len, char) if char == nil then char = ' ' end str = tostring(str) return str .. string.rep(char, len - #str) end function dmsg(name, value) crawl.mpr(pad(string.format(" %s: %s",tostring(name),tostring(value)),50)) end function getvar(what) return what end function debug(...) local name = arg[1] local value = arg[2] if arg['n'] == 1 then if name:find('%(') then dmsg(name, loadstring('return '..name)()) else dmsg(name, loadstring('return getvar('..name..')')()) end else dmsg(name, value) end end function stringify_array(arr) if type(arr) ~= 'table' then return type(arr) end local res = '{ ' for index, value in ipairs(arr) do if type(value) == "table" then value = stringify_array(value) end if type(index) == "string" then res = res .. index .. ' = ' .. tostring(value) .. ', ' else res = res .. tostring(value) .. ', ' end end return res .. '}' end function stringify_var(varname, var) return varname .. " = " .. tostring(var) .. "\n" end function stringify_sorted_array(arr, order, aname) local res = aname .. " = { " for _, index in pairs(order) do res = res .. '["' .. string.format("%s",index) .. '"] = "' .. string.format("%s",arr[index]) .. '", ' end res = res .. "}" res = res .. aname .. "_order = { " for _, index in pairs(order) do res = res .. '"' .. string.format("%s",index) .. '", ' end res = res .. "}" return res end function you_are_safe() return safe_from_invisible and you.feel_safe() and (you.poison_survival() > 0) and not pillardancing and not you.berserk() end autopickup_off_because = false function check_safe_invisible(newmessages) if newmessages:find("eactivating autopickup") then autopickup_off_because = "monster" elseif newmessages:find("utopickup is now off") then autopickup_off_because = "user" end if not options.autopick_on and autopickup_off_because == "monster" then safe_from_invisible = false else safe_from_invisible = true end end function check_safe_oos(newmessages) if newmessages:find("omething hits your") or newmessages:find("appears from outside your range of vision") then safe_from_oos = false else safe_from_oos = true end end function you_are_safe_reason() if not safe_from_invisible then return "invis" elseif not safe_from_oos then return "oos" elseif not you.feel_safe() then return "unsafe" elseif not (you.poison_survival() > 0) then return "pois" else return true end end function sendkeys(command) crawl.flush_input() crawl.sendkeys(command) crawl.flush_input() --coroutine.yield(true) end function should_rest() return you_poisoned or ( mp < (max_mp - autospent_mp) and you_god ~= "Pakellas" ) or ( hp < max_hp and you_race~="Deep Dwarf" and ( you_race~="Vampire" or you_hunger>0 ) ) or you_debuffed end function you_are_gourmand() return you.gourmand() or you_race=="Troll" or you_race=="Kobold" or you_race=="Ghoul" or you_race=="Felid" end function autoexplore() sendkeys('o') end function rest() sendkeys('5') end function is_in_inventory(str) for it in inventory() do if string.find(it.name(), str) then return true end end return false end function is_at(x,y,what) if not you.see_cell_no_trans(x,y) then return false end local pile = items.get_items_at(x,y) if pile ~= nil then for _, it in ipairs(pile) do for _, search in pairs(what) do if it.name():find(search) then return it end end end end return false end function inventory() return iter.invent_iterator:new(items.inventory()) end function last_item() local inv = items.inventory() debug("ni",inv[#inv].name()) end function floor_items() return iter.invent_iterator:new(you.floor_items()) end function is_equipped(where,str) if equipped_at[where] then return string.find(equipped_at[where].name(), str) else return false end end weapon_target_skill = 0 weapon_skill = "" weapon_type = "" function check_new_weapon() if equipped_at["weapon"] == nil or (equipped_at["weapon"].class(true) ~= "weapon" and equipped_at["weapon"].class(true) ~= "magical staff") then weapon_target_skill = 28 return true end weapon_skill = equipped_at["weapon"].weap_skill local maxdelay = equipped_at["weapon"].delay local mindelay = math.floor(maxdelay / 2) if weapon_skill == "Short Blades" and mindelay > 5 then mindelay = 5 end if mindelay > 7 then mindelay = 7 end if weapon_skill == "Crossbows" and mindelay < 10 then mindelay = 10 end mindelay = math.max(mindelay,maxdelay - 14) if mindelay < 3 then mindelay = 3 end weapon_target_skill = (maxdelay - mindelay) * 2 weapon_type = equipped_at["weapon"].subtype() end function is_alternative_weapon(str) local slot_a = items.inslot(items.letter_to_index("a")) local slot_b = items.inslot(items.letter_to_index("b")) local weapon = equipped_at["weapon"] if slot_a and not slot_a.cursed then slot_a = slot_a.name() else return false end if slot_b and not slot_b.cursed then slot_b = slot_b.name() else return false end if weapon and not weapon.cursed then weapon = weapon.name() else return false end if weapon ~= slot_a and weapon ~= slot_b then return false end return (weapon == slot_a and string.find(slot_b,str)) or (weapon == slot_b and string.find(slot_a,str)) end --Escapes the special characters in a string for pattern matching function escape(str) --Escapes parens and dash "()-" local escaped = str:gsub('[%(%)%-]','\\%1') --Removes any coloration parts of the string return (escaped:gsub('<[^<]*>','')) end local need_skills_opened = true function OpenSkills() if you.turns() == 0 and need_skills_opened then need_skills_opened = false sendkeys("m") end end function init_spells() local spell_list = {} for letter, spell_name in pairs(you.spell_table()) do spell_list[spell_name] = letter end return spell_list end known_spells = init_spells() function cast(what) sendkeys("z" .. known_spells[what]) end function init_abils() local abil_list = {} for letter, abil_name in pairs(you.ability_table()) do abil_list[abil_name] = letter end return abil_list end function abil(what) sendkeys("a" .. known_abils[what]) end known_abils = init_abils() } bindkey += [~] CMD_LUA_CONSOLE note_chat_messages = false ############# # Autofight # ############# # Do/don't throw stuff when autofighting autofight_throw = true autofight_throw_nomove = true # If true. items are autofired stopping at the monsters feet (same as firing using .) autofight_fire_stop = true fire_order = launcher, return fire_order += rock, javelin, tomahawk, stone # Prevent me from tab-fighting to death by keeping this high autofight_stop = 60 # Enables automagic attacks automagic_enable = false automagic_slot = a # Percentage of MP to stop automagic at automagic_stop = 50 # If set to true, resort to melee when automagic_stop is active automagic_fight = true ############## # Autopickup # ############## # Used # $ = gold # ? = scroll # ! = potion # : = book # " = jewellery # / = wand # % = food # } = miscellaneous # \ = rods # | = staves : if (you.god():find("Trog")) then autopickup += $?!:"/%}\ : else autopickup += $?!:"/%}\| : end # Unused # ) = weapon # ( = missiles # [ = armour # X = corpses # Allows easily dropping multiple items drop_mode += multi # Always show the full list of items when you pick up a stack pickup_mode += multi # Allows followers to pick up ANYTHING (take care not to lose artefacts) default_friendly_pickup += all # Set Alias for Autopickup Exceptions ae := autopickup_exceptions ae += useless_item, dangerous_item, evil_item # Don't pick up potion(s) of [coagulated] blood if you are not a vampire : if you.race() ~= "Vampire" then ae += potions? of.*blood : end # Autopickup artefacts ae += scroll of noise,>wand of random effects,>wand of flame ae += >staff of,>wand of slow,>wand of conf,>wand of poly,>wand of dig ae += >ring of fire,>ring of ice ae += >pizza,>royal jelly>,>beef jerky : if (you.race() ~= "Octopode") then ae += >ring of protection from,>ring of stealth,>ring of wiz : end : if (you.race() ~= "Mummy") then ae += >scroll of torment : end ############### # Autoexplore # ############### explore_stop -= stairs interrupt_travel -= hungry,sense_monster,mimic ############# # Interface # ############# view_delay = 0 skill_focus = toggle menu_colour ^= blue:(corpse|chunk) menu_colour ^= blue:bread ration menu_colour ^= blue:meat ration menu_colour ^= blue:jerk menu_colour ^= blue:jell menu_colour ^= blue:fruit menu_colour ^= blue:pizza menu_colour ^= darkgrey:inedible ############ # Messages # ############ msc := message_colour msc ^= mute:Things that are here: msc ^= mute:accepts your kill msc ^= mute:There is an open door here msc ^= mute:You open the door. msc ^= mute:You feel (dopey|clumsy|weakened) for a moment msc ^= mute:continue eating msc ^= mute:Your.*(says|yells) msc ^= mute:Your.*flickers and vanishes for a moment msc ^= mute:Your.*mumbles some strange words msc ^= mute:Your.*casts a spell msc ^= mute:Your.*gestures wildly while chanting more := force_more_message more ^= skill increases.*0 more ^= skill increases.*5 more ^= Shields skill increases to level 4 more ^= have mastered more ^= skill gains more ^= strange energies course more ^= a corruption grows more ^= The.*begins to chant more ^= drag you back to the ground more ^= is wielding.*distortion more ^= You are (blasted|electrocuted)! more -= You.*teleport [^f] more -= Your amulet of stasis more -= You don't.* that spell more -= You feel you are being watched by something more -= The.*shudders more -= You can now more -= Your?.*can no longer # Correct for minions more -= The acid corrodes your more -= A huge blade swings out and slices into you more -= silver sears you more -= dispelling energy hits you more -= Space bends around you more -= Space warps horribly around you more -= Terrible wounds (open|spread) all over you more -= The octopode crusher throws you more -= swoops through the air toward you more ^= dispelling energy hits you[^r] more ^= silver sears you[^r] more ^= Space bends around you[^r] more ^= Space warps horribly around you[^r] more ^= Terrible wounds (open|spread) all over you[^r] more += The octopode crusher throws you[^r] more += swoops through the air toward you[^r]