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]