Created
March 21, 2015 13:06
-
-
Save Pipeliner/f748898df1591d520961 to your computer and use it in GitHub Desktop.
stairdance, step one: retreat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This rcfile is elliptic's DCSS bot "qw", the first (and thus far only) bot | |
# to win DCSS with no human assistance. A substantial amount of code here was | |
# contributed by elliott or borrowed from N78291's bot "xw", and many others | |
# have contributed as well. | |
# For brief instructions and the most up-to-date version of qw, see | |
# https://github.com/elliptic/qw. | |
## qw's settings: | |
# | |
# Set this to true when playing online. | |
: DELAYED = false | |
# Delay per action in milliseconds. | |
# Set this to at least 100 or so when playing online. | |
: DELAY_TIME = 100 | |
# whether to start playing immediately when a new game is started | |
# unfortunately this doesn't work if the game starts with a -more- | |
: AUTO_START = false | |
# experimental: do second lair rune branch before depths | |
: EARLY_SECOND_RUNE = true | |
# do non-water lair rune first, or pick randomly? | |
: WATER_RUNE_SECOND = false | |
# experimental: dive to Abyss:3 to get fourth rune | |
: ABYSSAL_RUNE = true | |
# depth to attempt to dive in ziggurats (0 to turn off) | |
# if turned on, this causes annoying throttling delays when played online | |
# for some reason | |
: ZIG_DIVE = 0 | |
# burn books with Trog, may theoretically cause navigation problems | |
: BURN_BOOKS = true | |
# qw can play any starting combo. These ones are recommended: | |
combo = DDBe.handaxe, GrBe.handaxe, MiBe.handaxe | |
# For random berserkers, use these combos instead: | |
#combo = CeBe.handaxe, DDBe.handaxe, DEBe.handaxe, DrBe.handaxe, DsBe.handaxe | |
#combo += FeBe.claws, FoBe.handaxe, GrBe.handaxe, GhBe.claws, HaBe.falchion | |
#combo += HEBe.falchion, HOBe.handaxe, HuBe.handaxe, KoBe.mace, MfBe.spear | |
#combo += MiBe.handaxe, MuBe.handaxe, NaBe.handaxe, OgBe.mace, OpBe.handaxe | |
#combo += SpBe.shortsword, TeBe.handaxe, TrBe.claws, VSBe.handaxe, VpBe.handaxe | |
#combo = NaBe.handaxe, MiBe.handaxe, DsBe.handaxe, HOBe.handaxe | |
# For a totally random combo (with chosen weapon type), hyperqwcombo.rc can | |
# be created using hyperqwcombogen.sh and used: | |
#combo = | |
#include += hyperqwcombo.rc | |
## The accomplishments of qw: | |
# online wins: DDBe GrBe MiBe | |
# offline wins: DsBe HOBe HuBe MfBe NaBe TeBe VSBe | |
# (offline) runes: CeBe DrBe FoBe KoBe TrBe VpBe | |
# non-Be offline wins: DDAr DDAs DDCK DDFi DDGl DDMo DDNe DDSk DDSu DDTm DDWn DDWr | |
##################################### | |
# enum values :/ | |
: ENUM_MONS_PANDEMONIUM_LORD = 344 | |
: ATT_NEUTRAL = 1 | |
: ATT_HOSTILE = 0 | |
##################################### | |
# miscellaneous simple options | |
name = qw | |
restart_after_game = false | |
view_delay = 0 | |
use_animations = | |
clear_messages = true | |
travel_delay = -1 | |
explore_delay = -1 | |
travel_key_stop = false | |
default_manual_training = true | |
autopickup_no_burden = false | |
auto_exclude = | |
hp_warning = 0 | |
show_more = false | |
show_newturn_mark = false | |
list_rotten = false | |
force_more_message = | |
show_travel_trail = false | |
skill_focus = false | |
autoinscribe += slay:mikee | |
flush.failure = false | |
char_set = ascii | |
cset = cloud:xa4 | |
cset = item_orb:0 | |
use_fake_player_cursor = true | |
equip_unequip = true | |
dump_order = header,hiscore,stats,misc,mutations,skills,spells,inventory | |
dump_order += overview | |
dump_order += messages,screenshot,monlist,kills,notes,vaults,action_counts | |
ood_interesting = 6 | |
note_hp_percent = 25 | |
note_skill_levels = 1,3,6,9,12,15,18,21,24,27 | |
note_all_spells = true | |
#################################### | |
# not sure exactly how important or correct these settings are | |
explore_stop = | |
explore_stop += items,branches,portals,stairs,altars | |
explore_stop += greedy_visited_item_stack,greedy_pickup_smart | |
stop := runrest_stop_message | |
ignore := runrest_ignore_message | |
stop = | |
ignore = | |
ignore += .* | |
runrest_ignore_poison = 3:15 | |
runrest_ignore_monster += butterfly:1 | |
runrest_ignore_monster += orb of destruction:1 | |
#################################### | |
# These keys are useful to answer prompts and aren't critical for manual play | |
bindkey = [Y] CMD_NO_CMD_DEFAULT | |
bindkey = [N] CMD_NO_CMD_DEFAULT | |
bindkey = [B] CMD_NO_CMD_DEFAULT | |
bindkey = [.] CMD_NO_CMD_DEFAULT | |
#################################### | |
# Don't get interrupted! | |
: chk_interrupt_activity["blurry vision"] = function (iname, cause, extra) | |
: return nil | |
: end | |
#################################### | |
# autopickup/drop_filter stuff, just used for scrolls/potions/wands | |
autopickup = ?!/ | |
ae := autopickup_exceptions | |
df := drop_filter | |
ae = | |
df = | |
# keep: identify,teleportation,remove curse,enchant weapon, | |
# enchant armour,acquirement,recharging,holy word | |
ae += scrolls? of (summoning|vulnerability|brand weapon) | |
ae += scrolls? of (magic mapping|fog|fear|silence) | |
ae += scrolls? of (blinking|amnesia) | |
ae += scrolls? of (curse armour|curse jewellery|curse weapon) | |
ae += scrolls? of (immolation|noise|random uselessness|torment) | |
df += scrolls? of (summoning|vulnerability|brand weapon) | |
df += scrolls? of (magic mapping|fog|fear|silence) | |
df += scrolls? of (blinking|amnesia) | |
df += scrolls? of (curse armour|curse jewellery|curse weapon) | |
df += scrolls? of (immolation|noise|random uselessness|torment) | |
# keep: curing,heal wounds,haste,cancellation,resistance,experience, | |
# might,beneficial mutation,cure mutation,restore abilities | |
ae += potions? of (brilliance|magic|berserk rage) | |
ae += potions? of (flight|invisibility|agility) | |
ae += potions? of (ambrosia|decay|degeneration|mutation) | |
ae += potions? of (poison|lignification) | |
df += potions? of (brilliance|magic|berserk rage) | |
df += potions? of (flight|invisibility|agility) | |
df += potions? of (ambrosia|decay|degeneration|mutation) | |
df += potions? of (poison|lignification) | |
: if you.race() ~= "Vampire" then | |
ae += potions? of blood | |
df += potions? of blood | |
: end | |
# keep: heal wounds,hasting,teleportation | |
ae += wand of (random effects|slowing|magic darts|flame|frost|confusion) | |
ae += wand of (enslavement|paralysis|invisibility|lightning|fireball) | |
ae += wand of (cold|digging|disintegration|draining|fire|polymorph) | |
df += wand of (random effects|slowing|magic darts|flame|frost|confusion) | |
df += wand of (enslavement|paralysis|invisibility|lightning|fireball) | |
df += wand of (cold|digging|disintegration|draining|fire|polymorph) | |
################################################################ | |
# now the lua, beginning with options to set while qw is running | |
{ | |
-- maybe should add more mutes for watchability | |
function set_options() | |
crawl.setopt("confirm_butcher = always") | |
crawl.setopt("pickup_mode = multi") | |
crawl.setopt("message_colour += mute:Search for what") | |
crawl.setopt("message_colour += mute:Can't find anything") | |
crawl.setopt("message_colour += mute:Drop what") | |
crawl.setopt("message_colour += mute:Okay, then") | |
crawl.setopt("message_colour += mute:Use which ability") | |
crawl.setopt("message_colour += mute:Read which item") | |
crawl.setopt("message_colour += mute:Drink which item") | |
crawl.setopt("message_colour += mute:not good enough") | |
crawl.setopt("message_colour ^= mute:Unknown command") | |
crawl.enable_more(false) | |
end | |
function unset_options() | |
crawl.setopt("always_confirm_butcher = auto") | |
crawl.setopt("pickup_mode = auto") | |
crawl.setopt("message_colour -= mute:Search for what") | |
crawl.setopt("message_colour -= mute:Can't find anything") | |
crawl.setopt("message_colour -= mute:Drop what") | |
crawl.setopt("message_colour -= mute:Okay, then") | |
crawl.setopt("message_colour -= mute:Use which ability") | |
crawl.setopt("message_colour -= mute:Read which item") | |
crawl.setopt("message_colour -= mute:Drink which item") | |
crawl.setopt("message_colour -= mute:not good enough") | |
crawl.setopt("message_colour -= mute:Unknown command") | |
crawl.enable_more(true) | |
end | |
------------------------------------- | |
-- equipment valuation and autopickup | |
-- We assign a numerical value to all armour/weapon/jewellery, which | |
-- is used both for autopickup (so it has to work for unIDed items) and | |
-- for equipment selection. A negative value means we never want to use the | |
-- item. | |
-- The valuation functions normally assume the player just has their racial | |
-- intrinsic resists/abilities. When with_resists is set to true, the | |
-- functions take the player's current resists into account as well. Warning: | |
-- this may include resists granted by the object itself if it is equipped! | |
function equip_value(it, name, with_resists) | |
local class = it.class(true) | |
if class == "armour" then | |
return armour_value(it, name, with_resists) | |
elseif class == "weapon" then | |
return weapon_value(it, name, with_resists) | |
elseif class == "jewellery" then | |
if name:find("amulet") then | |
return amulet_value(it, name, with_resists) | |
else | |
return ring_value(it, name, with_resists) | |
end | |
end | |
return -1 | |
end | |
-- list of armour slots, this is used to normalize names for them and also | |
-- to iterate over the slots | |
good_slots = {cloak="Cloak", helmet="Helmet", | |
gloves="Gloves", boots="Boots", body="Armour", shield="Shield"} | |
function armour_value(it, name, with_resists) | |
local value = 0 | |
local ego = it.ego() | |
if it.artefact then | |
if it.fully_identified then | |
value = value + 100 -- random stuff is good on average | |
else | |
value = value + 400 -- it might be very good | |
end | |
ap = it.artprops | |
if ap and ap["rF"] then | |
if ap["rF"] > 0 then | |
if not with_resists or you.res_fire() < 2 then | |
value = value + 400 | |
end | |
else | |
value = value - 400 | |
end | |
end | |
if ap and ap["rPois"] and not intrinsic_rpois() then | |
if ap["rPois"] > 0 then | |
if not with_resists or you.res_poison() < 1 then | |
value = value + 200 | |
end | |
else | |
value = value - 200 | |
end | |
end | |
if ap and ap["SInv"] then | |
if not with_resists or not you.see_invisible() then | |
value = value + 400 -- we like this a lot | |
end | |
end | |
if ap and (ap["-Tele"] or ap["*Tele"]) and you.race() ~= "Formicid" then | |
return -1 | |
end | |
if ap and ap["*Rage"] and you.race() ~= "Mummy" | |
and you.race() ~= "Ghoul" and you.race() ~= "Formicid" then | |
return -1 | |
end | |
if name:find("Pondering") or name:find("hauberk") | |
or name:find("Bear Spirit") and you.race() == "Deep Dwarf" then | |
return -1 | |
end | |
if you.race() == "Deep Dwarf" and ap and ap["MP"] and ap["MP"] < 0 then | |
return -1 | |
end | |
elseif ego then -- names in armour_ego_name() | |
if ego == "fire resistance" then | |
if not with_resists or you.res_fire() < 2 then | |
value = value + 400 -- we like this a lot | |
end | |
elseif ego == "poison resistance" then | |
if not intrinsic_rpois() and (not with_resists or you.res_poison() < 1) then | |
value = value + 200 -- this is nice too | |
end | |
elseif ego == "see invisible" then | |
if not intrinsic_sinv() and (not with_resists or not you.see_invisible()) then | |
value = value + 400 -- we like this a lot | |
end | |
elseif ego == "running" then | |
value = value + 25 | |
elseif ego == "flying" and not intrinsic_flight() then | |
if not intrinsic_flight() then | |
value = value + 200 | |
end | |
elseif ego == "ponderousness" then | |
return -1 | |
elseif ego == "spirit shield" then | |
if you.race() == "Deep Dwarf" then | |
return -1 | |
end | |
end | |
elseif name:find("runed") or name:find("glowing") or name:find("dyed") or | |
name:find("embroidered") or name:find("shiny") then | |
value = value + 400 -- it might be very good | |
end | |
value = value + 100*it.ac | |
if it.plus then | |
value = value + 50*it.plus | |
end | |
st, _ = it.subtype() | |
if good_slots[st] == "Shield" then | |
if it.encumbrance == 1 then | |
if not want_buckler() then | |
return -1 | |
end | |
elseif not want_shield() then | |
return -1 | |
end | |
end | |
-- name always starts with {boots armour} here | |
if good_slots[st] == "Boots" then | |
if you.race() == "Centaur" then | |
if not (name:find("centaur barding") | |
or name:find("horse barding")) then | |
return -1 | |
end | |
elseif you.race() == "Naga" then | |
if not (name:find("naga barding") | |
or name:find("lightning scales")) then | |
return -1 | |
end | |
else | |
if name:find("barding") then | |
return -1 | |
end | |
end | |
end | |
if good_slots[st] == "Armour" then | |
evp = it.encumbrance | |
if intrinsic_dodgy() then | |
if evp > 7 then | |
return -1 | |
elseif evp >= 6 then | |
value = value - 250 | |
elseif evp >= 3 then | |
value = value - 100 | |
end | |
elseif intrinsic_heavy_dodgy() then | |
if evp >= 20 and intrinsic_heavy_dodgy() then | |
value = value - 200 | |
end | |
if name:find("pearl dragon") then | |
value = value + 200 | |
end | |
end | |
if name:find("hide") and not name:find("salamander hide") then | |
value = value + 100*it.ac - 50 | |
end | |
if name:find("swamp dragon") and not intrinsic_rpois() then | |
if not with_resists or you.res_poison() < 1 then | |
value = value + 100 | |
end | |
end | |
if name:find("storm dragon") and not intrinsic_relec() then | |
if not with_resists or you.res_shock() < 1 then | |
value = value + 100 | |
end | |
end | |
if name:find("fire dragon") then | |
if not with_resists or you.res_fire() < 2 then | |
value = value + 200 | |
end | |
end | |
if name:find("ice dragon") then | |
value = value - 300 | |
end | |
if name:find("gold dragon") then | |
value = value + 150 | |
end | |
end | |
return value | |
end | |
function weapon_value(it, name, with_resists) | |
local value = 1000 | |
if it.weap_skill ~= wskill() then | |
weap = items.equipped_at("Weapon") | |
if weap and weap.weap_skill == wskill() or wskill() == "Unarmed Combat" | |
or it.weap_skill == "Crossbows" or it.weap_skill == "Bows" | |
or it.weap_skill == "Slings" then | |
return -1 | |
end | |
end | |
if it.hands == 2 and want_buckler() then | |
return -1 | |
end | |
if it.artefact then | |
ap = it.artprops | |
if ap and (ap["-Tele"] or ap["*Tele"]) and you.race() ~= "Formicid" then | |
return -1 | |
end | |
if ap and ap["*Rage"] and you.race() ~= "Mummy" | |
and you.race() ~= "Ghoul" and you.race() ~= "Formicid" then | |
return -1 | |
end | |
if you.race() == "Deep Dwarf" and ap and ap["MP"] and ap["MP"] < 0 then | |
return -1 | |
end | |
end | |
if it.artefact and not it.fully_identified or | |
name:find("runed") or name:find("glowing") then | |
value = value + 500 -- it might be very good | |
end | |
local ego = it.ego() | |
if ego then -- names are mostly in weapon_brands_verbose[] | |
if ego == "distortion" then | |
return -1 | |
elseif ego == "holy wrath" then | |
if intrinsic_evil() then | |
return -1 | |
end | |
elseif ego == "vampirism" then | |
value = value + 500 -- this is what we want | |
elseif ego == "speed" then | |
value = value + 300 -- this is good too | |
elseif ego == "electrocution" then | |
value = value + 150 -- not bad | |
elseif ego == "flaming" or ego == "freezing" or ego == "draining" | |
or ego == "crushing" or ego == "slicing" | |
or ego == "piercing" or ego == "chopping" or ego == "slashing" then | |
value = value + 75 | |
elseif ego == "antimagic" then | |
if you.race() == "Vine Stalker" then | |
value = value - 300 | |
elseif you.race() ~= "Deep Dwarf" then | |
value = value + 75 | |
end | |
end | |
end | |
if it.plus then | |
value = value + 30*it.plus | |
end | |
value = value + 150*it.damage | |
if it.delay > 17 then | |
value = value - 150*(it.delay - 17) | |
end | |
if it.weap_skill ~= wskill() then | |
value = value / 10 | |
end | |
return value | |
end | |
function amulet_value(it, name, with_resists) | |
if it.artefact then return -1 end -- should parse inscription instead | |
if not name:find("amulet of") then return 100 end | |
if name:find("faith") then return 101 end -- so we never unequip this | |
if name:find("resist corrosion") then | |
if with_resists and you.res_corr() then | |
return -1 | |
else | |
return 9 | |
end | |
end | |
if name:find("resist mutation") then return 8 end | |
if name:find("clarity") then return 7 end | |
if name:find("regeneration") then | |
if you.race() == "Deep Dwarf" then | |
return -1 | |
else | |
return 6 | |
end | |
end | |
if name:find("warding") then return 5 end | |
if name:find("guardian spirit") and you.race() ~= "Deep Dwarf" then | |
return 4 | |
end | |
if name:find("rage") then return 3 end | |
return -1 | |
end | |
function ring_value(it, name, with_resists) | |
if it.artefact then return -1 end -- should parse inscription instead | |
if not name:find("ring of") then return 100 end | |
if it.plus and it.plus < 0 then return -1 end | |
local plus = 0 | |
if it.plus then plus = it.plus end | |
if not it.fully_identified then plus = 6 end | |
if name:find("see invisible") and not intrinsic_sinv() then | |
if with_resists and you.see_invisible() then | |
return -1 | |
else | |
return 50 | |
end | |
end | |
if name:find("protection from fire") then | |
if with_resists and you.res_fire() >= 2 then | |
return 16 | |
elseif with_resists and you.res_fire() == 1 then | |
return 47 | |
else | |
return 49 | |
end | |
end | |
if name:find("poison resistance") and not intrinsic_rpois() then | |
if with_resists and you.res_poison() >= 1 then | |
return -1 | |
else | |
return 48 | |
end | |
end | |
if name:find("protection from cold") then | |
if with_resists and you.res_cold() >= 1 then | |
return 15 | |
else | |
return 46 | |
end | |
end | |
if name:find("slaying") then return 20 + plus end | |
if name:find("protection from magic") then return 14 end | |
if name:find("protection") then return 19 + plus end | |
if name:find("evasion") then return 18 + plus end | |
if name:find("positive energy") then return 13 end | |
if name:find("strength") then return 7 + plus end | |
if name:find("dexterity") then return 7 + plus end | |
if name:find("sustain abilities") then return 2 end | |
return -1 | |
end | |
function autopickup(it, name) | |
if name:find("of Zot") then | |
return true | |
end | |
if it.is_useless then | |
return false | |
end | |
local class = it.class(true) | |
old_value = 0 | |
new_value = 0 | |
ring = false | |
if class == "armour" then | |
st, _ = it.subtype() | |
if good_slots[st] == nil then | |
return false | |
end | |
it2 = items.equipped_at(good_slots[st]) | |
elseif class == "weapon" then | |
it2 = items.equipped_at("Weapon") | |
elseif class == "jewellery" then | |
if name:find("amulet") then | |
it2 = items.equipped_at("Amulet") | |
else | |
it_rings = ring_list() | |
ring = true | |
end | |
elseif class == "food" then | |
return true | |
elseif class == "potion" or class == "scroll" or class == "wand" then | |
return | |
else | |
return false | |
end | |
new_value = equip_value(it, name) | |
if new_value < 0 then return false end | |
if (not ring) and it2 == nil or ring and empty_ring_slots() > 0 then | |
return true | |
else | |
if not ring then | |
old_value = equip_value(it2, it2:name()) | |
else | |
local r | |
for _, r in ipairs(it_rings) do | |
old_value2 = equip_value(r, r:name()) | |
if (not old_value) or old_value > old_value2 then | |
old_value = old_value2 | |
end | |
end | |
end | |
return (new_value > old_value) | |
end | |
end | |
clear_autopickup_funcs() | |
add_autopickup_func(autopickup) | |
----------------------------- | |
-- some global variables: | |
local dump_count = you.turns() + 100 - (you.turns() % 100) | |
local skill_count = you.turns() - (you.turns() % 20) | |
local danger | |
local immediate_danger | |
local where = you.where() | |
local where_shafted_from = nil | |
local expect_new_location | |
local expect_portal | |
local automatic = false | |
local ignore_list = { } | |
local failed_move = { } | |
local invisi_count = 0 | |
local next_delay = 100 | |
local dd_hw_meter = 0 | |
local sigmund_dx = 0 | |
local sigmund_dy = 0 | |
local invisi_sigmund = false | |
local stuck_turns = 0 | |
local stepped_on_lair = false | |
local kill_plant_mode = false | |
-- are these still necessary? | |
local did_move = false | |
local move_count = 0 | |
local did_move_towards_monster = 0 | |
local target_memory_x | |
local target_memory_y | |
local last_wait = 0 | |
local wait_count = 0 | |
local old_turn_count = you.turns() | |
local travel_destination = nil | |
local have_message = false | |
local read_message = true | |
local monster_array | |
local enemy_list | |
local upgrade_phase = false | |
local tactical_step | |
local tactical_reason | |
local did_first_turn = false | |
local stairdance_count = {} | |
------------------------------ | |
-- some tables with hardcoded data about branches/portals/monsters: | |
-- branch data: code, full name, where name | |
local branch_data = { | |
{"T", "the Ecumenical Temple", "Temple"}, | |
{"O", "the Orcish Mines", "Orc"}, | |
--{"E", "the Elven Halls", "Elf"}, | |
{"L", "the Lair", "Lair"}, | |
{"S", "the Swamp", "Swamp"}, | |
{"A", "the Shoals", "Shoals"}, | |
{"P", "the Snake Pit", "Snake"}, | |
{"N", "the Spider Nest", "Spider"}, | |
--{"M", "the Slime Pits", "Slime"}, | |
{"V", "the Vaults", "Vaults"}, | |
--{"C", "the Crypt", "Crypt"}, | |
--{"W", "the Tomb", "Tomb"}, | |
{"D", "the Dungeon", "D:"}, | |
{"U", "the Depths", "Depths"}, | |
{"Z", "Zot", "Zot"}, | |
} -- hack | |
-- portal data: where name, full name, feature name | |
local portal_data = { | |
--{"Bailey", "a flagged portal", "bailey"}, | |
--{"Bazaar", "gateway to a bazaar", "bazaar"}, | |
--{"IceCv", "a frozen archway", "ice_cave"}, | |
{"Ossuary", "covered staircase", "ossuary"}, | |
{"Sewer", "a glowing drain", "sewer"}, | |
--{"Volcano", "a dark tunnel", "volcano"}, | |
--{"WizLab", "a magical portal", "wizlab"}, | |
--{"Zig", "gateway to a ziggurat", "ziggurat"}, | |
--{"Lab", "labyrinth entrance", "labyrinth"}, | |
} -- hack | |
-- monster lists: XL cutoff (>= to ignore), monster name | |
-- if name is "desc" then look for the pattern given in the third field | |
-- inside the monster desc instead | |
-- berserk these | |
local scary_monsters = { | |
{3, "Terence"}, | |
{5, "gnoll"}, | |
{5, "Ijyb"}, | |
{5, "ice beast"}, | |
{5, "iguana"}, | |
{5, "Natasha"}, | |
{5, "Robin"}, | |
{7, "orc wizard"}, | |
{7, "Grinder"}, | |
{7, "Dowan"}, | |
{7, "Duvessa"}, | |
{7, "Menkaure"}, | |
{7, "Edmund"}, | |
{7, "Blork the orc"}, | |
{7, "Eustachio"}, | |
{7, "gnoll sergeant"}, | |
{10, "Prince Ribbit"}, | |
{10, "Pikel"}, | |
{10, "Crazy Yiuf"}, | |
{10, "Sigmund"}, | |
{10, "ogre"}, | |
{10, "two-headed ogre"}, | |
{10, "decayed bog body"}, | |
{12, "orc priest"}, | |
{12, "orc warrior"}, | |
{12, "troll"}, | |
{12, "cyclops"}, | |
{12, "hill giant"}, | |
{12, "spiny frog"}, | |
{12, "black mamba"}, | |
{12, "Snorg"}, | |
{12, "fire drake"}, | |
{12, "Harold"}, | |
{12, "komodo dragon"}, | |
{12, "Gastronok"}, | |
{12, "snapping turtle"}, | |
{12, "Urug"}, | |
{12, "Grum"}, | |
{12, "electric eel"}, | |
{12, "Nergalle"}, | |
{12, "jelly"}, | |
{12, "manticore"}, | |
{12, "guardian mummy"}, | |
{12, "Psyche"}, | |
{12, "oklob sapling"}, | |
{13, "blink frog"}, | |
{15, "death yak"}, | |
{15, "Maud"}, | |
{15, "Erica"}, | |
{15, "catoblepas"}, | |
{15, "orc knight"}, | |
{15, "boulder beetle"}, | |
{15, "swamp worm"}, | |
{17, "fire dragon"}, | |
{17, "ice dragon"}, | |
{17, "storm dragon"}, | |
{17, "ogre mage"}, | |
{17, "orc sorcerer"}, | |
{17, "orc high priest"}, | |
{17, "orc warlord"}, | |
{17, "dire elephant"}, | |
{17, "very large slime creature"}, | |
{17, "skeletal warrior"}, | |
{17, "Arachne"}, | |
{17, "deep troll"}, | |
{17, "thorn hunter"}, | |
{17, "sun demon"}, | |
{17, "white ugly thing"}, | |
{17, "white very ugly thing"}, | |
{20, "greater naga"}, | |
{20, "naga sharpshooter"}, | |
{20, "emperor scorpion"}, | |
{20, "Wiglaf"}, | |
{20, "Rupert"}, | |
{20, "Aizul"}, | |
{20, "desc", "hydra"}, | |
{20, "Azrael"}, | |
{20, "Frances"}, | |
{20, "Saint Roka"}, | |
{20, "Agnes"}, | |
{20, "Jory"}, | |
{20, "Nikola"}, | |
{20, "stone giant"}, | |
{20, "fire giant"}, | |
{20, "frost giant"}, | |
{20, "acid blob"}, | |
{20, "azure jelly"}, | |
{20, "Asterion"}, | |
{20, "spriggan defender"}, | |
{20, "spriggan air mage"}, | |
{20, "spriggan druid"}, | |
{20, "deep troll shaman"}, | |
{20, "Xtahua"}, | |
{20, "tengu reaver"}, | |
{20, "ettin"}, | |
{20, "Polyphemus"}, | |
{100, "deep elf annihilator"}, | |
{100, "deep elf sorcerer"}, | |
{100, "the Enchantress"}, | |
{100, "Vashnia"}, | |
{100, "Sojobo"}, | |
{100, "desc", "berserk[^e]"}, | |
{100, "Roxanne"}, | |
{100, "Norris"}, | |
{100, "Erolcha"}, | |
{100, "desc", "statue"}, | |
{100, "Nessos"}, | |
{100, "Sonja"}, | |
{100, "Louise"}, | |
{100, "Mennas"}, | |
{100, "Margery"}, | |
{100, "Frederick"}, | |
{100, "Boris"}, | |
{100, "Mara"}, | |
{100, "boggart"}, | |
{100, "lich"}, | |
{100, "ancient lich"}, | |
{100, "desc", "'s ghost"}, | |
{100, "desc", "' ghost"}, | |
{100, "desc", "'s illusion"}, | |
{100, "desc", "' illusion"}, | |
{100, "oklob plant"}, | |
{100, "hellion"}, | |
{100, "tormentor"}, | |
{100, "Hell Sentinel"}, | |
{100, "Ice Fiend"}, | |
{100, "Shadow Fiend"}, | |
{100, "Brimstone Fiend"}, | |
{100, "curse toe"}, | |
{100, "curse skull"}, | |
{100, "Tiamat"}, | |
{100, "titanic slime creature"}, | |
{100, "enormous slime creature"}, | |
{100, "titan"}, | |
{100, "orb of fire"}, | |
{100, "caustic shrike"}, | |
{100, "seraph"}, | |
} -- hack | |
-- these scary monsters are slow, so wait to berserk until they are adjacent | |
local scary_slow_monsters = { | |
{4, "worm"}, | |
{10, "goliath beetle"}, | |
{12, "boring beetle"}, | |
} -- hack | |
-- BiA these even at low piety | |
local bia_necessary_monsters = { | |
{15, "desc", "hydra"}, | |
{20, "orb spider"}, | |
{100, "desc", "statue"}, | |
} -- hack | |
-- BiA these | |
local bia_monsters = { | |
{15, "Rupert"}, | |
{15, "Azrael"}, | |
{15, "fire dragon"}, | |
{15, "ice dragon"}, | |
{15, "Snorg"}, | |
{15, "death yak"}, | |
{15, "red devil"}, | |
{17, "desc", "hydra"}, | |
{20, "orc warlord"}, | |
{20, "Aizul"}, | |
{20, "Frances"}, | |
{20, "Saint Roka"}, | |
{20, "Agnes"}, | |
{20, "Jory"}, | |
{20, "Norris"}, | |
{20, "Arachne"}, | |
{20, "Nikola"}, | |
{20, "Vashnia"}, | |
{20, "Asterion"}, | |
{20, "orb spider"}, | |
{20, "thorn hunter"}, | |
{20, "sun demon"}, | |
{20, "Polyphemus"}, | |
{100, "deep troll shaman"}, | |
{100, "spriggan air mage"}, | |
{100, "the Enchantress"}, | |
{100, "Sojobo"}, | |
{100, "Roxanne"}, | |
{100, "Erolcha"}, | |
{100, "desc", "statue"}, | |
{100, "Nessos"}, | |
{100, "Sonja"}, | |
{100, "Louise"}, | |
{100, "Mennas"}, | |
{100, "Margery"}, | |
{100, "Frederick"}, | |
{100, "Boris"}, | |
{100, "Mara"}, | |
{100, "boggart"}, | |
{100, "lich"}, | |
{100, "ancient lich"}, | |
{100, "desc", "'s ghost"}, | |
{100, "desc", "' ghost"}, | |
{100, "oklob plant"}, | |
{100, "Hell Sentinel"}, | |
{100, "Ice Fiend"}, | |
{100, "Brimstone Fiend"}, | |
{100, "Shadow Fiend"}, | |
{100, "Tiamat"}, | |
{100, "orb of fire"}, | |
{100, "caustic shrike"}, | |
{100, "seraph"}, | |
} -- hack | |
-- Trog's Hand these | |
local hand_monsters = { | |
{10, "Grinder"}, | |
{17, "orc sorcerer"}, | |
{17, "wizard"}, | |
{100, "ogre mage"}, | |
{100, "Rupert"}, | |
{100, "Aizul"}, | |
{100, "Norris"}, | |
{100, "Erolcha"}, | |
{100, "Louise"}, | |
{100, "lich"}, | |
{100, "ancient lich"}, | |
{100, "Kirke"}, | |
{100, "golden eye"}, | |
{100, "deep elf sorcerer"}, | |
{100, "deep elf demonologist"}, | |
{100, "sphinx"}, | |
{100, "great orb of eyes"}, | |
{100, "vault sentinel"}, | |
{100, "the Enchantress"}, | |
{100, "satyr"}, | |
{100, "vampire knight"}, | |
} -- hack | |
-- potion of resistance these | |
local fire_resistance_monsters = { | |
{100, "Margery"}, | |
{100, "orb of fire"}, | |
{100, "hellephant"}, | |
{100, "Xtahua"}, | |
} -- hack | |
local cold_resistance_monsters = { | |
{100, "Ice Fiend"}, | |
} -- hack | |
local elec_resistance_monsters = { | |
{20, "storm dragon"}, | |
{20, "desc", "black draconian"}, | |
{100, "electric golem"}, | |
} -- hack | |
local pois_resistance_monsters = { | |
{100, "swamp drake"}, | |
} -- hack | |
----------------------------------------- | |
-- player functions | |
-- "intrinsics" that shouldn't change over the course of the game: | |
function intrinsic_rpois() | |
local sp = you.race() | |
if sp == "Gargoyle" or sp == "Naga" or sp == "Ghoul" or sp == "Mummy" then | |
return true | |
end | |
return false | |
end | |
function intrinsic_relec() | |
local sp = you.race() | |
if sp == "Gargoyle" then | |
return true | |
end | |
return false | |
end | |
function intrinsic_sinv() | |
local sp = you.race() | |
if sp == "Naga" or sp == "Felid" or sp == "Formicid" or sp == "Vampire" then | |
return true | |
end | |
return false | |
end | |
function intrinsic_flight() -- or swimming | |
local sp = you.race() | |
if sp == "Gargoyle" or sp == "Tengu" or sp == "Black Draconian" or | |
sp == "Merfolk" or sp == "Octopode" then | |
return true | |
end | |
return false | |
end | |
function intrinsic_fumble() | |
local sp = you.race() | |
if sp == "Merfolk" or sp == "Octopode" or sp == "Grey Draconian" or | |
sp == "Centaur" or sp == "Naga" or sp == "Troll" or sp == "Ogre" then | |
return false | |
end | |
return true | |
end | |
function intrinsic_evil() | |
local sp = you.race() | |
if sp == "Demonspawn" or sp == "Mummy" or sp == "Ghoul" or | |
sp == "Vampire" then | |
return true | |
end | |
return false | |
end | |
-- not exactly gourmand, but close enough | |
function intrinsic_gourmand() | |
return (you.race() == "Kobold" or you.race() == "Troll" | |
or you.race() == "Felid") | |
end | |
-- these next few functions determine armour/dodging skilling | |
function intrinsic_dodgy() | |
local sp = you.race() | |
return (sp == "Deep Elf" or sp:find("Draconian") or sp == "Felid" | |
or sp == "Halfling" or sp == "High Elf" or sp == "Kobold" | |
or sp == "Merfolk" or sp == "Ogre" or sp == "Octopode" | |
or sp == "Spriggan" or sp == "Troll") | |
end | |
function intrinsic_heavy_dodgy() | |
return (you.race() == "Minotaur" or you.race() == "Deep Dwarf") | |
end | |
function intrinsic_heavy() | |
return (not intrinsic_dodgy() and not intrinsic_heavy_dodgy()) | |
end | |
function intrinsic_want_dex() | |
return (intrinsic_dodgy() or intrinsic_heavy_dodgy()) | |
end | |
function want_buckler() | |
if you.race() == "Felid" then | |
return false | |
end | |
if wskill() == "Short Blades" or wskill() == "Unarmed Combat" then | |
return true | |
end | |
if you.race() == "Formicid" or you.race() == "Halfling" | |
or you.race() == "Kobold" then | |
return true | |
end | |
return false | |
end | |
function want_shield() | |
if not want_buckler() then | |
return false | |
end | |
return (you.race() == "Troll" or you.race() == "Formicid") | |
end | |
-- used for backgrounds who don't get to choose a weapon | |
function weapon_choice() | |
sp = you.race() | |
if sp == "Felid" or sp == "Troll" or sp == "Ghoul" then | |
return "Unarmed Combat" | |
elseif sp == "Halfling" or sp == "High Elf" then | |
return "Long Blades" | |
elseif sp == "Ogre" or sp == "Kobold" then | |
return "Maces & Flails" | |
elseif sp == "Merfolk" then | |
return "Polearms" | |
elseif sp == "Spriggan" then | |
return "Short Blades" | |
else | |
return "Axes" | |
end | |
end | |
function wskill() | |
-- cache in case you unwield a weapon somehow | |
if c_persist.cached_wskill then | |
return c_persist.cached_wskill | |
end | |
weap = items.equipped_at("Weapon") | |
if weap and weap.weap_skill ~= "Short Blades" then | |
c_persist.cached_wskill = weap.weap_skill | |
else | |
c_persist.cached_wskill = weapon_choice() | |
end | |
return c_persist.cached_wskill | |
end | |
function max_rings() | |
if you.race() == "Octopode" then | |
return 8 | |
else | |
return 2 | |
end | |
end | |
-- other player functions | |
function hp_is_low(percentage) | |
local hp, mhp = you.hp() | |
return (100*hp <= percentage*mhp) | |
end | |
function meph_immune() | |
-- should also check clarity and unbreathing | |
return (you.res_poison() >= 1) | |
end | |
function miasma_immune() | |
-- this isn't all the cases, I know | |
return (you.race() == "Gargoyle" or you.race() == "Vine Stalker" | |
or you.race() == "Ghoul" or you.race() == "Mummy") | |
end | |
function in_portal() | |
for _, value in ipairs(portal_data) do | |
if value[1] == where then | |
return true | |
end | |
end | |
return false | |
end | |
function get_feat_name(where_name) | |
for _, value in ipairs(portal_data) do | |
if where_name == value[1] then | |
return value[3] | |
end | |
end | |
end | |
function cur_branch() | |
for _, value in ipairs(branch_data) do | |
if where:find(value[3]) then | |
return value[1] | |
end | |
end | |
end | |
function found_branch(br) | |
if br == "D" then | |
return true | |
end | |
for _, value in ipairs(branch_data) do | |
if value[1] == br then | |
if travel.find_deepest_explored(value[3]) > 0 then | |
return true | |
else | |
return false | |
end | |
end | |
end | |
return false | |
end | |
function in_branch(br) | |
for _, value in ipairs(branch_data) do | |
if value[1] == br then | |
if where:find(value[3]) then | |
return true | |
else | |
return false | |
end | |
end | |
end | |
return false | |
end | |
function is_traversable(x,y) | |
local feat = view.feature_at(x,y) | |
return feat ~= "unseen" and travel.feature_traversable(feat) | |
end | |
function is_cornerish(x,y) | |
if is_traversable(x+1,y+1) or is_traversable(x+1,y-1) | |
or is_traversable(x-1,y+1) or is_traversable(x-1,y-1) then | |
return false | |
end | |
return ((is_traversable(x+1,y) or is_traversable(x-1,y)) | |
and (is_traversable(x,y+1) or is_traversable(x,y-1))) | |
end | |
function is_solid(x,y) | |
local feat = view.feature_at(x,y) | |
return feat == "unseen" or travel.feature_solid(feat) | |
end | |
function dangerous_to_rest() | |
if danger then | |
return true | |
end | |
for x = -1,1 do | |
for y = -1,1 do | |
if view.feature_at(x,y) == "slimy_wall" then | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function dangerous_terrain_adjacent() | |
if you.flying() then | |
return false | |
end | |
for i = -1,1 do | |
for j = -1,1 do | |
if view.feature_at(i,j) == "deep_water" or | |
view.feature_at(i,j) == "lava" then | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function transformed() | |
return (you.transform() ~= "") | |
end | |
function can_read() | |
if you.berserk() or you.confused() or you.silenced() | |
or you.status("engulfed (cannot breathe)") then | |
return false | |
end | |
return true | |
end | |
function can_drink() | |
if you.berserk() or you.race() == "Mummy" or you.transform() == "bat" | |
or you.transform() == "lich" then | |
return false | |
end | |
return true | |
end | |
function can_zap() | |
if you.berserk() or you.confused() or transformed() then | |
return false | |
end | |
local x = you.mutation("MP-powered wands") | |
if x > 0 then | |
local a,b = you.mp() | |
if a < 3*x then | |
return false | |
end | |
end | |
return true | |
end | |
function can_berserk() | |
return (not (you.berserk() or you.confused() or you.silenced() or | |
you.exhausted() or you.mesmerised() or | |
too_hungry_to_berserk() or | |
you.piety_rank() < 1 or | |
you.god() ~= "Trog" or | |
you.transform() == "tree" or | |
you.transform() == "wisp" or | |
you.transform() == "lich" or | |
you.status("afraid") or | |
you.race() == "Mummy" or you.race() == "Ghoul" | |
or you.race() == "Formicid")) | |
end | |
function can_hand() | |
return (not (you.berserk() or you.confused() or you.silenced() or | |
you.regenerating() or | |
you.hunger_name() == "starving" or you.piety_rank() < 2 or | |
you.god() ~= "Trog")) | |
end | |
function can_bia() | |
return (not (you.berserk() or you.confused() or you.silenced() or | |
you.hunger_name() == "starving" or you.piety_rank() < 4 or | |
you.god() ~= "Trog")) | |
end | |
function can_teleport() | |
return (not you.berserk() and not you.teleporting() and not you.anchored() | |
and not you.confused() | |
and you.transform() ~= "tree" | |
and you.race() ~= "Formicid") | |
end | |
function too_hungry_to_berserk() | |
if you.race() == "Vampire" then | |
return (you.hunger_name() ~= "almost alive" | |
and you.hunger_name() ~= "very full" | |
and you.hunger_name() ~= "full") | |
end | |
return (you.hunger_name() == "starving" or | |
you.hunger_name() == "near starving" or | |
you.hunger_name() == "very hungry") | |
end | |
function player_speed_num() | |
local num = 3 | |
if you.god() == "Cheibriados" then | |
num = 1 | |
elseif you.race() == "Spriggan" or you.race() == "Centaur" then | |
num = 4 | |
elseif you.race() == "Naga" then | |
num = 2 | |
end | |
if you.hasted() or you.berserk() then | |
num = num + 1 | |
end | |
if you.slowed() then | |
num = num - 1 | |
end | |
return num | |
end | |
----------------------------------------- | |
-- monster functions | |
function mon_speed_num(m) | |
local sdesc = m:speed_description() | |
local num | |
if sdesc == "extremely fast" then | |
num = 5 | |
elseif sdesc == "very fast" then | |
num = 4 | |
elseif sdesc == "fast" or sdesc == "normal" then | |
num = 3 | |
elseif sdesc == "slow" then | |
num = 2 | |
elseif sdesc == "very slow" then | |
num = 1 | |
end | |
if m:status("fast") then | |
num = num + 1 | |
end | |
if m:status("slow") then | |
num = num - 1 | |
end | |
return num | |
end | |
function is_fast(m) | |
return (mon_speed_num(m) > player_speed_num()) | |
end | |
function is_ranged(m) | |
if m:has_known_ranged_attack() then | |
return true | |
end | |
local name = m:name() | |
if name == "flayed ghost" or name == "shock serpent" or name == "Maurice" | |
or name:find("draconian") and not name:find("grey draconian") | |
or name:find("crimson imp") then | |
return true | |
end | |
return false | |
end | |
function sense_immediate_danger() | |
local e | |
for _,e in ipairs(enemy_list) do | |
if supdist(e.x,e.y) <= 2 then | |
return true | |
elseif supdist(e.x,e.y) <= 3 and e.m:reach_range() > 2 then | |
return true | |
elseif is_ranged(e.m) then | |
return true | |
end | |
end | |
return false | |
end | |
function sense_danger(r, no_ignored) | |
local e | |
for _,e in ipairs(enemy_list) do | |
if supdist(e.x,e.y) <= r then | |
if (not no_ignored) or is_candidate_for_attack(e.x,e.y,no_ignored) then | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function sense_sigmund() | |
local e | |
for _,e in ipairs(enemy_list) do | |
if e.m:name() == "Sigmund" then | |
sigmund_dx = e.x | |
sigmund_dy = e.y | |
return | |
end | |
end | |
end | |
function initialize_monster_array() | |
monster_array = {} | |
local x | |
for x = -8,8 do | |
monster_array[x] = {} | |
end | |
end | |
function update_monster_array() | |
local x,y | |
enemy_list = {} | |
--c_persist.mlist = {} | |
for x = -8,8 do | |
for y = -8,8 do | |
monster_array[x][y] = monster.get_monster_at(x, y) | |
if is_candidate_for_attack(x, y) then | |
entry = {} | |
entry.x = x | |
entry.y = y | |
entry.m = monster_array[x][y] | |
table.insert(enemy_list, entry) | |
--table.insert(c_persist.mlist, entry.m:name()) | |
end | |
end | |
end | |
end | |
function check_monsters(r, mlist) | |
local e | |
local xl = you.xl() | |
for _,e in ipairs(enemy_list) do | |
if supdist(e.x,e.y) <= r then | |
if not contains_string_in(e.m:name(), {"skeleton", "zombie", | |
"simulacrum", "spectral"}) then | |
local name = e.m:name() | |
local desc = e.m:desc() | |
for _, value in ipairs(mlist) do | |
if xl < value[1] and (value[2] == name or value[2] == "desc" | |
and desc:find(value[3])) then | |
return true | |
end | |
end | |
end | |
end | |
end | |
return false | |
end | |
function count_bia(r) | |
local x, y | |
local i = 0 | |
for x = -r,r do | |
for y = -r,r do | |
m = monster_array[x][y] | |
if m and m:is_safe() and m:is("berserk") | |
and contains_string_in(m:name(), {"ogre","giant","bear","troll"}) then | |
i = i+1 | |
end | |
end | |
end | |
return i | |
end | |
function count_pan_lords(r) | |
local e | |
local i = 0 | |
for _,e in ipairs(enemy_list) do | |
if e.m:type() == ENUM_MONS_PANDEMONIUM_LORD then | |
i = i+1 | |
end | |
end | |
return i | |
end | |
-- should only be called for adjacent squares | |
function monster_in_way(dx,dy) | |
m = monster_array[dx][dy] | |
return (m and (m:attitude() <= ATT_NEUTRAL and not kill_plant_mode or | |
m:attitude() > ATT_NEUTRAL and | |
(m:is_constricted() or m:is_caught() | |
or view.feature_at(0,0) == "deep_water" | |
or view.feature_at(0,0) == "lava" | |
or view.feature_at(0,0) == "trap_zot"))) | |
end | |
function tabbable_square(x,y) | |
if view.feature_at(x,y) ~= "unseen" and view.is_safe_square(x,y) then | |
local m = monster_array[x][y] | |
if not m or not m:is_firewood() then | |
return true | |
end | |
end | |
return false | |
end | |
function try_move(dx, dy) | |
if view.is_safe_square(dx, dy) and not view.withheld(dx, dy) and | |
not monster_in_way(dx,dy) then | |
return delta_to_vi(dx, dy) | |
else | |
return nil | |
end | |
end | |
function will_tab(cx, cy, ex, ey) | |
local dx = ex - cx | |
local dy = ey - cy | |
if abs(dx) <= 1 and abs(dy) <= 1 then | |
return true | |
end | |
local function attempt_move(fx, fy) | |
if fx == 0 and fy == 0 then return end | |
if supdist(cx+fx,cy+fy) > 8 then return end | |
if tabbable_square(cx+fx, cy+fy) then | |
return will_tab(cx+fx, cy+fy, ex, ey) | |
end | |
end | |
local move = nil | |
if abs(dx) > abs(dy) then | |
if abs(dy) == 1 then move = attempt_move(sign(dx), 0) end | |
if move == nil then move = attempt_move(sign(dx), sign(dy)) end | |
if move == nil then move = attempt_move(sign(dx), 0) end | |
if move == nil and abs(dx) > abs(dy)+1 then | |
move = attempt_move(sign(dx), 1) end | |
if move == nil and abs(dx) > abs(dy)+1 then | |
move = attempt_move(sign(dx), -1) end | |
if move == nil then move = attempt_move(0, sign(dy)) end | |
elseif abs(dx) == abs(dy) then | |
move = attempt_move(sign(dx), sign(dy)) | |
if move == nil then move = attempt_move(sign(dx), 0) end | |
if move == nil then move = attempt_move(0, sign(dy)) end | |
else | |
if abs(dx) == 1 then move = attempt_move(0, sign(dy)) end | |
if move == nil then move = attempt_move(sign(dx), sign(dy)) end | |
if move == nil then move = attempt_move(0, sign(dy)) end | |
if move == nil and abs(dy) > abs(dx)+1 then | |
move = attempt_move(1, sign(dy)) end | |
if move == nil and abs(dy) > abs(dx)+1 then | |
move = attempt_move(-1, sign(dy)) end | |
if move == nil then move = attempt_move(sign(dx), 0) end | |
end | |
if move == nil then return false end | |
return move | |
end | |
function get_monster_info(dx, dy) | |
m = monster_array[dx][dy] | |
if not m then return nil end | |
name = m:name() | |
info = {} | |
info.distance = (abs(dx) > abs(dy)) and -abs(dx) or -abs(dy) | |
if 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 | |
elseif you.caught() or you.confused() then info.attack_type = 0 | |
else info.attack_type = view.can_reach(dx, dy) and 1 or 0 end | |
end | |
info.can_attack = (info.attack_type > 0) and 1 or 0 | |
info.safe = m:is_safe() and -1 or 0 | |
info.constricting_you = m:is_constricting_you() and 1 or 0 | |
info.very_stabbable = (m:stabbability() >= 1) and 1 or 0 | |
-- info.stabbable = m:is(0) and 1 or 0 | |
info.injury = m:damage_level() | |
info.threat = m:threat() | |
info.orc_priest_wizard = (name == "orc priest" or | |
name == "orc wizard") and 1 or 0 | |
return info | |
end | |
function compare_monster_info(m1, m2) | |
flag_order = {"can_attack", "safe", "distance", "constricting_you", | |
"very_stabbable", "injury", "threat", "orc_priest_wizard"} | |
for i, flag in ipairs(flag_order) do | |
if m1[flag] > m2[flag] then return true end | |
if m1[flag] < m2[flag] then return false end | |
end | |
return false | |
end | |
function is_candidate_for_attack(x, y, no_untabbable) | |
if supdist(x,y) > 8 then | |
return false | |
end | |
m = monster_array[x][y] | |
if not m or m:attitude() > ATT_NEUTRAL then | |
return false | |
end | |
if m:name() == "butterfly" | |
or m:name() == "orb of destruction" then | |
return false | |
end | |
if m:is_firewood() then | |
if not string.find(m:name(), "ballistomycete") then | |
return false | |
end | |
end | |
if no_untabbable then | |
if will_tab(0,0,x,y) then | |
remove_ignore(x,y) | |
else | |
add_ignore(x,y) | |
return false | |
end | |
end | |
return true | |
end | |
function count_ranged(cx, cy, r) | |
local e | |
local i = 0 | |
for _,e in ipairs(enemy_list) do | |
local dist = supdist(cx-e.x,cy-e.y) | |
if dist > 1 and dist <= r then | |
if dist == 2 and (e.m:reach_range() > 2 or is_fast(e.m)) | |
or is_ranged(e.m) then | |
i = i+1 | |
end | |
end | |
end | |
return i | |
end | |
function count_nearby(cx, cy, r) | |
local x, y | |
local i = 0 | |
for x = -r,r do | |
for y = -r,r do | |
if supdist(x,y) > 0 and is_candidate_for_attack(cx+x, cy+y) then | |
i = i+1 | |
end | |
end | |
end | |
return i | |
end | |
function count_slow_nearby(cx, cy, r) | |
local x, y | |
local i = 0 | |
for x = -r,r do | |
for y = -r,r do | |
if supdist(x,y) > 0 and is_candidate_for_attack(cx+x, cy+y) | |
then | |
m = monster_array[cx+x][cy+y] | |
if mon_speed_num(m) < player_speed_num() | |
and not is_ranged(m) and m:reach_range() <= 2 then | |
i = i+1 | |
end | |
end | |
end | |
end | |
return i | |
end | |
function distance_to_enemy(cx, cy) | |
local dist = 10 | |
local e | |
for _,e in ipairs(enemy_list) do | |
if supdist(cx-e.x,cy-e.y) < dist then | |
dist = supdist(cx-e.x,cy-e.y) | |
end | |
end | |
return dist | |
end | |
----------------------------------------- | |
-- item functions | |
function inventory() | |
return iter.invent_iterator:new(items.inventory()) | |
end | |
function at_feet() | |
return iter.invent_iterator:new(you.floor_items()) | |
end | |
function slot(x) | |
if type(x) == "userdata" then | |
return x.slot | |
elseif type(x) == "string" then | |
return items.letter_to_index(x) | |
else | |
return x | |
end | |
end | |
function letter(x) | |
if type(x) == "userdata" then | |
return items.index_to_letter(x.slot) | |
elseif type(x) == "number" then | |
return items.index_to_letter(x) | |
else | |
return x | |
end | |
end | |
function item(x) | |
if type(x) == "number" then | |
return items.inslot(x) | |
elseif type(x) == "string" then | |
return items.inslot(items.letter_to_index(x)) | |
else | |
return x | |
end | |
end | |
function ring_list() | |
rings = {} | |
if you.race() ~= "Octopode" then | |
if items.equipped_at("Left Ring") then | |
table.insert(rings, items.equipped_at("Left Ring")) | |
end | |
if items.equipped_at("Right Ring") then | |
table.insert(rings, items.equipped_at("Right Ring")) | |
end | |
return rings | |
end | |
for it in inventory() do | |
if it.equipped and it.class(true) == "jewellery" and it:name():find("ring") | |
then | |
table.insert(rings, it) | |
end | |
end | |
return rings | |
end | |
function empty_ring_slots() | |
return max_rings() - table.getn(ring_list()) | |
end | |
function find_item(cls,name) | |
if cls == "wand" then return find_wand(name) end | |
for it in inventory() do | |
if it.class(true) == cls and it.name():find(name) then | |
return items.index_to_letter(it.slot) | |
end | |
end | |
end | |
function find_wand(name) | |
for it in inventory() do | |
if it.class(true) == "wand" and it.name():find(name) and | |
not it.name():find("empty") and (it.plus == nil or it.plus > 0) then | |
return items.index_to_letter(it.slot) | |
end | |
end | |
end | |
function count_item(cls,name) | |
f = find_item(cls, name) | |
if f then | |
return item(f).quantity | |
end | |
return 0 | |
end | |
function have_reaching() | |
local wp = items.equipped_at("weapon") | |
return wp and wp.reach_range == 8 and not wp.is_melded | |
end | |
function target_shield_skill() | |
local shield = items.equipped_at("Shield") | |
if not shield then | |
return 0 | |
end | |
local size = 5 | |
if you.race() == "Kobold" or you.race() == "Halfling" then | |
size = 7 | |
elseif you.race() == "Spriggan" then | |
size = 9 | |
elseif you.race() == "Troll" or you.race() == "Ogre" | |
or you.race() == "Formicid" or you.race() == "Naga" | |
or you.race() == "Centaur" then | |
size = 3 | |
end | |
return (shield.encumbrance * size) | |
end | |
function min_delay_skill() | |
weap = items.equipped_at("Weapon") | |
if not weap then | |
return 26 | |
end | |
if weap.weap_skill == "Short Blades" and weap.delay == 12 then | |
return 14 | |
end | |
local mindelay = math.floor(weap.delay / 2) | |
if mindelay > 7 then | |
mindelay = 7 | |
end | |
return 2 * (weap.delay - mindelay) | |
end | |
function cleaving() | |
weap = items.equipped_at("Weapon") | |
if weap and weap.weap_skill == "Axes" then | |
return true | |
end | |
return false | |
end | |
function armour_ac() | |
arm = items.equipped_at("Armour") | |
if arm then | |
return arm.ac | |
else | |
return 0 | |
end | |
end | |
function on_corpses() | |
for it in at_feet() do | |
if string.find(it.name(), "corpse") then | |
return true | |
end | |
end | |
return false | |
end | |
function on_bottleable_corpses() | |
for it in at_feet() do | |
if string.find(it.name(), "corpse") | |
and food.bottleable(it) then | |
return true | |
end | |
end | |
return false | |
end | |
function on_dangerous_corpse() | |
for it in at_feet() do | |
if string.find(it.name(), "corpse") then | |
return food.dangerous(it) | |
end | |
end | |
return false | |
end | |
function on_edible_corpse() | |
for it in at_feet() do | |
if string.find(it.name(), "corpse") then | |
return food.edible(it) | |
end | |
end | |
return false | |
end | |
function on_valuable_corpse() | |
if not intrinsic_dodgy() then | |
return false | |
end | |
for it in at_feet() do | |
if it.name() == "mottled dragon corpse" or | |
it.name() == "steam dragon corpse" then | |
return true | |
end | |
end | |
return false | |
end | |
function bad_food(it) | |
return food.dangerous(it) | |
end | |
function can_swap(equip_slot) | |
local it = items.equipped_at(equip_slot) | |
if it and it.cursed then | |
return false | |
end | |
if it and it.ego() == "flying" and | |
(view.feature_at(0,0) == "deep_water" or | |
view.feature_at(0,0) == "lava") then | |
return false | |
end | |
return true | |
end | |
-- plural form, e.g. "Scrolls" | |
-- or invoke with item_class="name_callback" and provide callback for name | |
function see_item(item_class, r, name_callback) | |
for x = -r,r do | |
for y = -r,r do | |
-- crawl.mpr("(" .. x .. ", " .. y .. "): " .. view.feature_at(x,y) .."\r") | |
local is = items.get_items_at(x, y) | |
if (is ~= nil) and (#is > 0) and (you.see_cell(x,y)) then | |
for ind,i in pairs(is) do | |
local iname = i:name() | |
if (i:class(true) == item_class) or ((item_class == "name_callback") and name_callback(iname)) then | |
return true | |
end | |
end | |
end | |
end | |
end | |
return false | |
end | |
-- Matches all unindentified books | |
function is_spellbook(book_name) | |
return (book_name ~= nil) and (book_name:find("book") ~= nil) | |
end | |
function see_spellbooks_to_burn() | |
return see_item("name_callback", 8, is_spellbook) | |
and not see_item("name_callback", 0, is_spellbook) | |
end | |
----------------------------------------- | |
-- "plans" - functions that take actions, and logic to determine which actions | |
-- to take. | |
-- Every function that might take an action should return as follows: | |
-- true if tried to do something | |
-- false if didn't do anything | |
-- nil if should be rerun (currently only used by cascades, be careful | |
-- of loops... this is poorly tested) | |
function get_target() | |
local e, bestx, besty, best_info, new_info | |
bestx = 0 | |
besty = 0 | |
best_info = nil | |
for _,e in ipairs(enemy_list) do | |
if not util.contains(failed_move, 20*e.x+e.y) then | |
if is_candidate_for_attack(e.x, e.y, true) then | |
new_info = get_monster_info(e.x, e.y) | |
if (not best_info) or | |
compare_monster_info(new_info, best_info) then | |
bestx = e.x | |
besty = e.y | |
best_info = new_info | |
end | |
end | |
end | |
end | |
return bestx, besty, best_info | |
end | |
function should_rest() | |
if you.confused() or you.berserk() or transformed() then | |
return true | |
end | |
if dangerous_to_rest() then | |
return false | |
end | |
return (hp_is_low(90) and | |
(you.race() ~= "Deep Dwarf" and you.hunger_name() ~= "bloodless" | |
or you.regenerating()) | |
or you.slowed() or you.exhausted() or you.teleporting() | |
or you.status("manticore barbs") | |
or you.silencing() or you.corrosion() > 0) | |
end | |
function want_permafood() | |
if you.race() == "Mummy" or you.transform() == "lich" then | |
return false | |
end | |
if you.race() == "Vampire" then | |
return (you.hunger_name() ~= "almost alive" | |
and you.hunger_name() ~= "very full" | |
and you.hunger_name() ~= "full") | |
end | |
if you.race() == "Spriggan" then | |
return (you.hunger_name() ~= "completely stuffed" | |
and you.hunger_name() ~= "very full") | |
end | |
return (you.hunger_name() == "near starving" | |
or you.hunger_name() == "very hungry" | |
and (where == "Snake:5" or where == "Spider:5" | |
or you.xl() >= 18 or intrinsic_gourmand()) | |
or you.hunger_name() == "starving") | |
end | |
function want_chunk() | |
if you.race() == "Spriggan" or you.race() == "Mummy" | |
or you.race() == "Vampire" or you.transform() == "lich" then | |
return false | |
end | |
if you.race() == "Ghoul" and you.rot() > 0 then | |
return true | |
end | |
if intrinsic_gourmand() and (you.hunger_name() == "not hungry" | |
or you.hunger_name() == "full" | |
or you.hunger_name() == "very full") then | |
return true | |
end | |
return you.hunger_name() == "hungry" or | |
you.hunger_name() == "very hungry" or want_permafood() | |
end | |
function rest() | |
magic("s") | |
next_delay = 5 | |
end | |
function attack() | |
local success = false | |
failed_move = { } | |
while not success do | |
bestx, besty, best_info = get_target() | |
if best_info == nil then | |
return false | |
end | |
success = make_attack(bestx, besty, best_info) | |
end | |
return true | |
end | |
function pray() | |
magic("p") | |
end | |
function chop() | |
magic("ccq") | |
end | |
function berserk() | |
use_ability("Berserk") | |
end | |
function hand() | |
use_ability("Trog's Hand") | |
end | |
function bia() | |
use_ability("Brothers in Arms") | |
end | |
-- Will fail if the book is in a non-fire cloud already. | |
function burn_spellbooks() | |
use_ability("Burn Spellbooks") | |
end | |
function plan_bia() | |
if can_bia() and want_to_bia() then | |
bia() | |
return true | |
end | |
return false | |
end | |
function plan_resistance() | |
if not you.extra_resistant() and not you.teleporting() | |
and want_resistance() then | |
return drink_by_name("resistance") | |
end | |
return false | |
end | |
function plan_hand() | |
if can_hand() and want_to_hand() and not you.teleporting() then | |
hand() | |
return true | |
end | |
return false | |
end | |
function dd_prefer_hand() | |
return (you.piety_rank() == 6 or you.base_mp() < 3) | |
end | |
function plan_dd_hand_for_healing() | |
if you.race() ~= "Deep Dwarf" then | |
return false | |
end | |
local hp,mhp = you.hp() | |
if mhp - hp >= 30 and can_hand() and | |
dd_prefer_hand() then | |
hand() | |
return true | |
end | |
return false | |
end | |
function plan_abyss_hand() | |
local hp,mhp = you.hp() | |
if mhp - hp >= 30 and can_hand() then | |
hand() | |
return true | |
end | |
return false | |
end | |
function plan_orbrun_hand() | |
local hp,mhp = you.hp() | |
if mhp - hp >= 30 and can_hand() then | |
hand() | |
return true | |
end | |
return false | |
end | |
function plan_dd_recharge_teleport() | |
if you.race() ~= "Deep Dwarf" then | |
return false | |
end | |
if you.berserk() or you.confused() or you.hunger_name() == "starving" then | |
return false | |
end | |
if count_item("scroll","teleportation") > 0 then | |
return false | |
end | |
if immediate_danger and not want_to_teleport() then | |
return false | |
end | |
local wand_letter | |
for it in inventory() do | |
if it.class(true) == "wand" and it.name():find("teleportation") then | |
if (((not it.fully_identified) and not it.name():find("empty")) or | |
it.plus and it.plus > 0) then | |
return false | |
else | |
wand_letter = letter(it) | |
end | |
end | |
end | |
if not wand_letter then | |
return false | |
end | |
for it in inventory() do | |
if it.class(true) == "scroll" and can_read() and | |
it.name():find("recharging") then | |
oldname = item(wand_letter).name() | |
if read2(it, wand_letter) then | |
say("RECHARGING " .. oldname .. ".") | |
return true | |
end | |
end | |
end | |
local mp,mmp = you.mp() | |
if mp > 0 and you.base_mp() > 0 then | |
for letter, abil in pairs(you.ability_table()) do | |
if abil == "Device Recharging" then | |
say("MP-RECHARGING " .. item(wand_letter).name() .. ".") | |
-- swap slots in case ability fails | |
items.swap_slots(slot(wand_letter), slot('Y')) | |
magic("a" .. letter .. "Y") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_dd_orbrun_recharge_hasting() | |
if you.race() ~= "Deep Dwarf" then | |
return false | |
end | |
if danger then | |
return false | |
end | |
if you.berserk() or you.confused() or you.hunger_name() == "starving" then | |
return false | |
end | |
if count_item("potion","haste") > 0 then | |
return false | |
end | |
local wand_letter | |
for it in inventory() do | |
if it.class(true) == "wand" and it.name():find("hasting") then | |
if (((not it.fully_identified) and not it.name():find("empty")) or | |
it.plus and it.plus > 0) then | |
return false | |
else | |
wand_letter = letter(it) | |
end | |
end | |
end | |
if not wand_letter then | |
return false | |
end | |
for it in inventory() do | |
if it.class(true) == "scroll" and can_read() and | |
it.name():find("recharging") then | |
oldname = item(wand_letter).name() | |
if read2(it, wand_letter) then | |
say("RECHARGING " .. oldname .. ".") | |
return true | |
end | |
end | |
end | |
local mp,mmp = you.mp() | |
if mp > 0 and you.base_mp() > 0 then | |
for letter, abil in pairs(you.ability_table()) do | |
if abil == "Device Recharging" then | |
say("MP-RECHARGING " .. item(wand_letter).name() .. ".") | |
-- swap slots in case ability fails | |
items.swap_slots(slot(wand_letter), slot('Y')) | |
magic("a" .. letter .. "Y") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_dd_recharge_heal_wounds() | |
if you.race() ~= "Deep Dwarf" then | |
return false | |
end | |
if you.berserk() or you.confused() or you.hunger_name() == "starving" then | |
return false | |
end | |
local charge_bound = immediate_danger and 1 or 3 | |
local wand_letter | |
local wand_count = 0 | |
for it in inventory() do | |
if it.class(true) == "wand" and it.name():find("heal wounds") then | |
if (((not it.fully_identified) and not it.name():find("empty")) or | |
it.plus and it.plus > charge_bound) then | |
wand_count = wand_count + 1 | |
else | |
wand_letter = letter(it) | |
end | |
end | |
end | |
if immediate_danger and wand_count > 0 or not wand_letter then | |
return false | |
end | |
for it in inventory() do | |
if it.class(true) == "scroll" and can_read() and | |
it.name():find("recharging") then | |
oldname = item(wand_letter).name() | |
if read2(it, wand_letter) then | |
say("RECHARGING " .. oldname .. ".") | |
return true | |
end | |
end | |
end | |
local mp,mmp = you.mp() | |
if mp > 0 and you.base_mp() > 0 then | |
for letter, abil in pairs(you.ability_table()) do | |
if abil == "Device Recharging" then | |
say("MP-RECHARGING " .. item(wand_letter).name() .. ".") | |
-- swap slots in case ability fails | |
items.swap_slots(slot(wand_letter), slot('Y')) | |
magic("a" .. letter .. "Y") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_cure_bad_poison() | |
if not (danger or you.race() == "Deep Dwarf") then | |
return false | |
end | |
local hp, mhp = you.hp() | |
if you.poison_survival() <= hp - 60 then | |
if drink_by_name("curing") then | |
say("(to cure bad poison)") | |
return true | |
end | |
end | |
return false | |
end | |
function plan_heal_wounds() | |
if want_to_heal_wounds() then | |
return heal_wounds() | |
end | |
return false | |
end | |
function plan_orbrun_heal_wounds() | |
if want_to_orbrun_heal_wounds() then | |
return heal_wounds() | |
end | |
return false | |
end | |
function plan_orbrun_haste() | |
if want_to_orbrun_haste() then | |
return haste() | |
end | |
return false | |
end | |
function plan_orbrun_might() | |
if want_to_orbrun_might() then | |
return might() | |
end | |
return false | |
end | |
function heal_wounds() | |
if you.mutation("no device heal") >= 3 then | |
return false | |
end | |
if you.race() == "Deep Dwarf" then | |
if drink_by_name("heal wounds") then | |
dd_hw_meter = dd_hw_meter + 100 | |
return true | |
end | |
end | |
if selfzap_by_name("heal wounds") then | |
dd_hw_meter = dd_hw_meter + 100 | |
return true | |
end | |
if you.race() ~= "Deep Dwarf" then | |
if drink_by_name("heal wounds") then | |
dd_hw_meter = dd_hw_meter + 100 | |
return true | |
end | |
end | |
return false | |
end | |
function haste() | |
if you.hasted() or you.race() == "Formicid" then | |
return false | |
end | |
if selfzap_by_name("hasting") then | |
return true | |
end | |
return drink_by_name("haste") | |
end | |
function might() | |
if you.mighty() then | |
return false | |
end | |
return drink_by_name("might") | |
end | |
function cloud_is_dangerous(cloud) | |
if cloud == "flame" or cloud == "fire" then | |
return (you.res_fire() < 1) | |
elseif cloud == "noxious fumes" then | |
return (not meph_immune()) | |
elseif cloud == "freezing vapour" then | |
return (you.res_cold() < 1) | |
elseif cloud == "poison gas" then | |
return (you.res_poison() < 1) | |
elseif cloud == "calcifying dust" then | |
return (you.race() ~= "Gargoyle") | |
elseif cloud == "foul pestilence" then | |
return (not miasma_immune()) | |
elseif cloud == "seething chaos" or cloud == "mutagenic fog" then | |
return true | |
end | |
return false | |
end | |
function assess_square(x,y) | |
a = {} | |
-- distance tu current square | |
a.supdist = supdist(x,y) | |
-- can we move there? | |
a.can_move = (a.supdist == 0) or not view.withheld(x,y) | |
and not monster_in_way(x,y) | |
and is_traversable(x,y) | |
and not is_solid(x,y) | |
if not a.can_move then | |
return a | |
end | |
cloud = view.cloud_at(x,y) | |
-- nonadjacent monsters who might be able to attack you (ranged/reaching/fast) | |
a.ranged = count_ranged(x,y,8) | |
-- adjacent monsters | |
a.adjacent = count_nearby(x,y,1) | |
-- corners - avoid these if not cleaving | |
a.cornerish = is_cornerish(x,y) | |
if have_reaching() then | |
-- slow adjacent nonranged monsters, for kiting | |
a.slow_adjacent = count_slow_nearby(x,y,1) | |
end | |
-- distance to nearest enemy | |
a.enemy_distance = distance_to_enemy(x,y) | |
-- will we fumble if we try to attack from this square? | |
a.fumble = (not you.flying() and view.feature_at(x,y) == "shallow_water" | |
and intrinsic_fumble()) | |
-- will we be slow if we move into this square? | |
a.slow = (not you.flying() and view.feature_at(x,y) == "shallow_water" | |
and you.race() ~= "Merfolk" and you.race() ~= "Octopode") | |
-- is the square safe to step in? (checks traps+clouds) | |
a.safe = view.is_safe_square(x,y) | |
cloud = view.cloud_at(x,y) | |
-- would we want to move out of a cloud? note that we don't worry about | |
-- weak clouds if monsters are around | |
a.cloud_safe = (cloud == nil) or a.safe | |
or danger and not cloud_is_dangerous(cloud) | |
return a | |
end | |
-- returns a string explaining why moving a1->a2 is preferable to not moving | |
-- possibilities are: | |
-- cloud - stepping out of harmful cloud | |
-- water - stepping out of shallow water when it would cause fumbling | |
-- reaching - kiting slower monsters with reaching | |
-- outnumbered - stepping away from a square adjacent to multiple monsters | |
-- (when not cleaving) | |
function step_reason(a1,a2) | |
if not (a2.can_move and a2.safe and a2.supdist > 0) then | |
return false | |
elseif not a1.cloud_safe then | |
return "cloud" | |
elseif a2.fumble or a2.slow then | |
return false | |
elseif a1.fumble then | |
if a2.ranged > a1.ranged and a2.enemy_distance > a1.enemy_distance then | |
return false | |
else | |
return "water" | |
end | |
elseif have_reaching() and a1.slow_adjacent > 0 and a2.adjacent == 0 | |
and a2.ranged == 0 then | |
return "reaching" | |
elseif cleaving() then | |
return false | |
elseif a1.adjacent == 1 then | |
return false | |
elseif a2.adjacent + a2.ranged <= a1.adjacent + a1.ranged - 2 then | |
return "outnumbered" | |
else | |
return false | |
end | |
end | |
-- determines whether moving a0->a2 is an improvement over a0->a1 | |
-- assumes that these two moves have already been determined to be better | |
-- than not moving, with given reason | |
function step_improvement(reason,a1,a2) | |
if reason == "water" and a2.enemy_distance < a1.enemy_distance then | |
return true | |
elseif reason == "water" and a2.enemy_distance > a1.enemy_distance then | |
return false | |
elseif a2.adjacent+a2.ranged < a1.adjacent+a1.ranged then | |
return true | |
elseif a2.adjacent+a2.ranged > a1.adjacent+a1.ranged then | |
return false | |
elseif cleaving() and a2.ranged < a1.ranged then | |
return true | |
elseif cleaving() and a2.ranged > a1.ranged then | |
return false | |
elseif a2.enemy_distance < a1.enemy_distance then | |
return true | |
elseif a2.enemy_distance > a1.enemy_distance then | |
return false | |
elseif a1.cornerish and not a2.cornerish then | |
return true | |
else | |
return false | |
end | |
end | |
-- this function always returns false because it doesn't actually take the | |
-- tactical step, just chooses it | |
function plan_choose_tactical_step() | |
tactical_step = nil | |
tactical_reason = "none" | |
if you.confused() or you.berserk() or you.constricted() | |
or you.transform() == "tree" or you.transform() == "fungus" then | |
return false | |
end | |
local a0 = assess_square(0,0) | |
if a0.cloud_safe and not (a0.fumble and sense_danger(3)) | |
and (not have_reaching() or a0.slow_adjacent == 0) | |
and (a0.adjacent <= 1 or cleaving()) then | |
return false | |
end | |
local bestx,besty,bestreason | |
local besta = nil | |
local x,y | |
local a | |
local reason | |
for x = -1,1 do | |
for y = -1,1 do | |
if supdist(x,y) > 0 then | |
a = assess_square(x,y) | |
reason = step_reason(a0,a) | |
if reason then | |
if besta == nil or step_improvement(reason,besta,a) then | |
bestx = x | |
besty = y | |
besta = a | |
bestreason = reason | |
end | |
end | |
end | |
end | |
end | |
if besta then | |
tactical_step = delta_to_vi(bestx,besty) | |
tactical_reason = bestreason | |
--say("Stepping ~*~*~tactically~*~*~.") | |
--magic(delta_to_vi(bestx,besty) .. "Y") | |
--say("tactics: " .. delta_to_vi(bestx,besty)) | |
--stop() | |
--return true | |
end | |
return false | |
end | |
function plan_cloud_step() | |
if tactical_reason == "cloud" then | |
say("Stepping ~*~*~tactically~*~*~ (" .. tactical_reason .. ").") | |
magic(tactical_step .. "Y") | |
return true | |
end | |
return false | |
end | |
function plan_water_step() | |
if tactical_reason == "water" then | |
say("Stepping ~*~*~tactically~*~*~ (" .. tactical_reason .. ").") | |
magic(tactical_step .. "Y") | |
return true | |
end | |
return false | |
end | |
function plan_other_step() | |
if tactical_reason ~= "none" then | |
say("Stepping ~*~*~tactically~*~*~ (" .. tactical_reason .. ").") | |
magic(tactical_step .. "Y") | |
return true | |
end | |
return false | |
end | |
function plan_berserk() | |
if can_berserk() and want_to_berserk() then | |
berserk() | |
return true | |
end | |
return false | |
end | |
function plan_burn_spellbooks() | |
if want_to_burn_spellbooks() then | |
burn_spellbooks() | |
return true | |
end | |
return false | |
end | |
function want_to_bia() | |
if check_monsters(8, bia_necessary_monsters) or you.piety_rank() > 4 and | |
((want_to_berserk() and not can_berserk()) or | |
check_monsters(8, bia_monsters)) then | |
if count_bia(4) == 0 and not you.teleporting() then | |
return true | |
end | |
end | |
return false | |
end | |
function dd_want_to_teleport() | |
--say("HW: " .. dd_hw_meter) | |
return ((immediate_danger and dd_hw_meter > 300 | |
or immediate_danger and you.corrosion() >= 3 | |
or (you.slowed() or too_hungry_to_berserk()) | |
and want_to_berserk() | |
and not can_berserk()) | |
and count_bia(4) == 0) | |
end | |
function want_to_teleport() | |
if you.race() == "Deep Dwarf" then | |
return dd_want_to_teleport() | |
end | |
return ((immediate_danger and you.corrosion() >= 3 | |
or (you.slowed() or too_hungry_to_berserk()) | |
and want_to_berserk() | |
and not can_berserk()) | |
and count_bia(4) == 0) | |
end | |
function want_to_orbrun_teleport() | |
return (hp_is_low(33) and sense_danger(2)) | |
end | |
function want_to_orbrun_holy_word() | |
return (danger and want_to_orbrun_heal_wounds()) | |
end | |
function dd_want_to_heal_wounds() | |
if (not danger) and (you.regenerating() or dd_prefer_hand()) then | |
return false | |
end | |
local hp,mhp = you.hp() | |
if immediate_danger then | |
return (hp_is_low(25) or (hp_is_low(50) and mhp - hp >= 30) or | |
(mhp - hp >= 20 and hp <= 20)) | |
else | |
if dd_prefer_hand() and (not you.teleporting()) and not hp_is_low(66) then | |
return false | |
end | |
return (hp_is_low(25) or | |
(mhp - hp >= 30) or | |
(mhp - hp >= 20 and hp <= 20)) | |
end | |
end | |
function want_to_heal_wounds() | |
if you.race() == "Deep Dwarf" then | |
return dd_want_to_heal_wounds() | |
end | |
return (danger and hp_is_low(25)) | |
end | |
function want_to_orbrun_heal_wounds() | |
if danger then | |
return (hp_is_low(25) or hp_is_low(50) and you.teleporting()) | |
else | |
return (hp_is_low(50)) | |
end | |
end | |
function want_to_orbrun_haste() | |
return ((count_pan_lords(8) > 0) or check_monsters(8, scary_monsters)) | |
end | |
function want_to_orbrun_might() | |
return want_to_orbrun_haste() | |
end | |
function want_resistance() | |
return (check_monsters(8, fire_resistance_monsters) and you.res_fire() < 3 | |
or check_monsters(8, cold_resistance_monsters) and you.res_cold() < 3 | |
or check_monsters(8, elec_resistance_monsters) and you.res_shock() < 1 | |
or check_monsters(8, pois_resistance_monsters) and | |
you.res_poison() < 1) | |
end | |
function want_to_hand() | |
return check_monsters(8, hand_monsters) | |
end | |
function want_to_berserk() | |
return (hp_is_low(50) and sense_danger(2) and | |
(you.race() ~= "Deep Dwarf" or dd_want_to_heal_wounds()) or | |
check_monsters(2, scary_monsters) or | |
check_monsters(1, scary_slow_monsters) or | |
(invisi_sigmund and not options.autopick_on)) | |
end | |
function want_to_stay_in_abyss() | |
if you.xl() > 20 and where:find("Abyss") and count_permafood() <= 5 and ABYSSAL_RUNE then | |
say("Giving up on abyssal rune: low on food") | |
ABYSSAL_RUNE = false | |
end | |
return you.xl() > 20 and not you.have_rune("abyssal") and | |
not hp_is_low(60) and you.num_runes() == 3 and ABYSSAL_RUNE | |
end | |
function plan_retreat_to_upstairs() | |
crawl.mpr(tostring(danger)) | |
crawl.mpr(tostring(immediate_danger)) | |
crawl.mpr(tostring(count_ranged(0,0,8))) | |
crawl.mpr(where) | |
if where == "D:1" or in_portal() then | |
return false | |
end | |
local feat = view.feature_at(0,0) | |
if feat:find("stone_stairs_up") or feat == "escape_hatch_up" then | |
return false | |
end | |
if danger and not immediate_danger and count_ranged(0,0,8) == 0 then | |
say("Retreating!") | |
sc() | |
magic("X<\r") | |
return true | |
end | |
return false | |
end | |
function plan_wait_for_melee() | |
if sense_danger(1) or (have_reaching() and sense_danger(2)) | |
or (not options.autopick_on) or | |
you.berserk() or you.have_orb() or count_bia(8) > 0 or | |
not view.is_safe_square(0,0) or | |
view.feature_at(0,0) == "shallow_water" and not you.flying() then | |
wait_count = 0 | |
return false | |
end | |
if you.turns() >= last_wait + 10 then | |
wait_count = 0 | |
end | |
if (not danger) or wait_count >= 10 then | |
return false | |
end | |
count = 0 | |
sleeping_count = 0 | |
local e | |
for _,e in ipairs(enemy_list) do | |
if is_ranged(e.m) then | |
wait_count = 0 | |
return false | |
end | |
if e.m:reach_range() > 2 and supdist(e.x,e.y) <= 2 then | |
wait_count = 0 | |
return false | |
end | |
if will_tab(0,0,e.x,e.y) and not | |
(e.m:name() == "wandering mushroom" or | |
e.m:name():find("ballistomycete") or | |
e.m:name():find("vortex") or | |
e.m:desc():find("fleeing")) then | |
count = count + 1 | |
if e.m:desc():find("sleeping") then | |
sleeping_count = sleeping_count + 1 | |
end | |
end | |
end | |
if count == 0 then | |
return false | |
end | |
-- say "Waiting for monsters to approach." | |
if sleeping_count == 0 then | |
wait_count = wait_count + 1 | |
end | |
last_wait = you.turns() | |
if plan_cure_poison_rotting() then | |
return true | |
end | |
magic("s") | |
return true | |
end | |
function plan_attack() | |
if danger then | |
if attack() then | |
return true | |
end | |
end | |
return false | |
end | |
function plan_eat_chunk() | |
if want_chunk() then | |
for it in inventory() do | |
if string.find(it.name(), "chunk") and | |
not bad_food(it) then | |
magic("ee") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_eat_permafood() | |
if where == "Zot:5" then | |
return plan_orbrun_eat_permafood() | |
end | |
if want_permafood() then | |
if eat_permafood() then | |
return true | |
end | |
end | |
return false | |
end | |
function plan_orbrun_eat_permafood() | |
if you.hunger_name() ~= "completely stuffed" and | |
you.hunger_name() ~= "very full" and | |
you.hunger_name() ~= "almost alive" then | |
if eat_permafood() then | |
return true | |
end | |
end | |
return false | |
end | |
function plan_eat_anyway() | |
if you.hunger_name() == "very hungry" then | |
if eat_permafood() then | |
return true | |
end | |
end | |
return false | |
end | |
function drink_blood() | |
if drink_by_name("coagulated blood") then | |
return true | |
end | |
return drink_by_name("blood") | |
end | |
function eat_permafood() | |
if you.race() == "Vampire" then | |
return drink_blood() | |
end | |
local l | |
local max_prefer = 0 | |
local food_name | |
for it in inventory() do | |
if it.class(true) == "food" and not bad_food(it) then | |
local name = it.name() | |
local prefer | |
if name:find("ration") or name:find("royal") then | |
prefer = 1 | |
elseif name:find("chunk") then | |
prefer = 0 | |
else | |
prefer = 3 | |
end | |
if prefer > max_prefer then | |
l = items.index_to_letter(it.slot) | |
max_prefer = prefer | |
food_name = it.name() | |
end | |
end | |
end | |
if max_prefer > 0 then | |
items.swap_slots(items.letter_to_index(l), items.letter_to_index('e'), | |
false) | |
say("EATING " .. food_name .. ".") | |
magic("ee") | |
return true | |
end | |
end | |
function count_permafood() | |
local s = 0 | |
for it in inventory() do | |
if it.class(true) == "food" and not bad_food(it) and not food.ischunk(it) then | |
s = s + it.quantity | |
end | |
end | |
return s | |
end | |
function plan_rest() | |
if should_rest() then | |
rest() | |
return true | |
end | |
return false | |
end | |
function plan_orbrun_rest() | |
if you.confused() or you.slowed() or | |
you.berserk() or you.teleporting() or you.silencing() or | |
transformed() then | |
rest() | |
return true | |
end | |
return false | |
end | |
function plan_abyss_rest() | |
local hp,mhp = you.hp() | |
if you.confused() or you.slowed() or | |
you.berserk() or you.teleporting() or you.silencing() or | |
transformed() or (hp < mhp) and you.regenerating() then | |
rest() | |
return true | |
end | |
return false | |
end | |
function want_pray() | |
return (you.god() == "Trog") | |
end | |
function want_to_burn_spellbooks() | |
return BURN_BOOKS and (you.god() == "Trog") and can_read() and see_spellbooks_to_burn() | |
end | |
function want_blood() | |
return (count_item("potion","of blood") < 10) | |
end | |
function vp_handle_corpses() | |
if want_blood() and on_bottleable_corpses() then | |
chop() | |
elseif want_permafood() and on_edible_corpse() and not on_dangerous_corpse() then | |
magic("eY") | |
elseif want_pray() | |
and not view.feature_at(0,0):find("altar") | |
and not you.silenced() and not on_valuable_corpse() then | |
pray() | |
else | |
chop() | |
end | |
return true | |
end | |
function plan_handle_corpses() | |
if not on_corpses() then | |
return false | |
end | |
if you.race() == "Vampire" then | |
return vp_handle_corpses() | |
end | |
if want_pray() and (not want_chunk() or on_dangerous_corpse()) | |
and not string.find(view.feature_at(0, 0), "altar") | |
and not you.silenced() and not on_valuable_corpse() then | |
pray() | |
else | |
chop() | |
end | |
return true | |
end | |
function plan_find_altar() | |
if not want_altar() then | |
return false | |
end | |
magic(control('f') .. "@altar&&trog" .. "\ra\r") | |
return true | |
end | |
function plan_abandon_god() | |
if you.god() ~= "No God" and you.god() ~= "Trog" then | |
magic("aXYY") | |
return true | |
end | |
return false | |
end | |
function plan_unwield_weapon() | |
if wskill() ~= "Unarmed Combat" then | |
return false | |
end | |
if not items.equipped_at("Weapon") then | |
return false | |
end | |
magic("w-") | |
return true | |
end | |
function plan_join_god() | |
if not want_altar() then | |
return false | |
end | |
if view.feature_at(0, 0) == "altar_trog" then | |
if you.silenced() then | |
rest() | |
else | |
magic("pYY") | |
end | |
return true | |
end | |
return false | |
end | |
function plan_find_corpses() | |
magic(control('f') .. "@corpse$&&!!rott&&!!skel" .. "\ra\r") | |
return true | |
end | |
function plan_autoexplore() | |
magic("o") | |
return true | |
end | |
function plan_drop_filtered_items() | |
magic("d,,\r") | |
upgrade_phase = false | |
return true | |
end | |
function plan_quaff_id() | |
for it in inventory() do | |
if it.class(true) == "potion" and it.quantity > 1 and | |
not it.fully_identified then | |
return drink(it) | |
end | |
end | |
return false | |
end | |
function plan_read_id() | |
if not can_read() then | |
return false | |
end | |
for it in inventory() do | |
if it.class(true) == "scroll" and | |
not it.fully_identified then | |
items.swap_slots(it.slot, items.letter_to_index('Y'), false) | |
weap = items.equipped_at("Weapon") | |
scroll_letter = 'Y' | |
if weap and not weap:name():find("vamp") then | |
scroll_letter = items.index_to_letter(weap.slot) | |
items.swap_slots(weap.slot, items.letter_to_index('Y'), false) | |
end | |
if you.race() ~= "Felid" then | |
return read2(scroll_letter, ".Y" .. string.char(27) .. "YB") | |
else | |
return read2(scroll_letter, ".Y" .. string.char(27) .. "YF") | |
end | |
end | |
end | |
return false | |
end | |
function plan_use_id_scrolls() | |
if not can_read() then | |
return false | |
end | |
local id_scroll | |
for it in inventory() do | |
if it.class(true) == "scroll" and it.name():find("identify") then | |
id_scroll = it | |
end | |
end | |
if not id_scroll then | |
return false | |
end | |
local oldslots = { } | |
local newslots = {[0] = 'B', [1] = 'N', [2] = 'Y'} -- harmless keys | |
local count = 0 | |
for it in inventory() do | |
if it.class(true) == "wand" and not it.fully_identified and | |
(it.name():find("empty") or it.name():find("teleportation") or | |
it.name():find("heal wounds") and you.race() ~= "Vine Stalker" or | |
it.name():find("hasting")) then | |
oldname = it.name() | |
if read2(id_scroll, letter(it)) then | |
say("IDENTIFYING " .. oldname) | |
return true | |
end | |
end | |
end | |
for it in inventory() do | |
if it.class(true) == "jewellery" and not it.fully_identified then | |
oldname = it.name() | |
if read2(id_scroll, letter(it)) then | |
say("IDENTIFYING " .. oldname) | |
return true | |
end | |
end | |
end | |
if id_scroll.quantity > 1 then | |
for it in inventory() do | |
if it.class(true) == "potion" and not it.fully_identified then | |
oldname = it.name() | |
if read2(id_scroll, letter(it)) then | |
say("IDENTIFYING " .. oldname) | |
return true | |
end | |
end | |
end | |
end | |
return false | |
end | |
function body_armour_is_great(arm) | |
name = arm:name() | |
if intrinsic_dodgy() then | |
return (name:find("dragon")) | |
else | |
return (name:find("gold dragon") or name:find("crystal plate") | |
or name:find("plate armour of fire") | |
or name:find("pearl dragon") and intrinsic_heavy_dodgy()) | |
end | |
end | |
function body_armour_is_good(arm) | |
if in_branch("Z") then | |
return true | |
end | |
name = arm:name() | |
if intrinsic_dodgy() then | |
return (name:find("of resistance") or name:find("of fire")) | |
else | |
return (name:find("pearl dragon") or name:find("plate")) | |
end | |
end | |
function plan_use_good_consumables() | |
for it in inventory() do | |
if it.class(true) == "scroll" and can_read() then | |
if it.name():find("acquirement") then | |
if you.race() ~= "Felid" then | |
return read2(it, " b") | |
else | |
return read2(it, " f") | |
end | |
elseif it.name():find("enchant weapon") then | |
weapon = items.equipped_at("weapon") | |
if weapon and not weapon.artefact and | |
weapon.plus < 9 then | |
oldname = weapon.name() | |
if read2(it, letter(weapon)) then | |
say("ENCHANTING " .. oldname .. ".") | |
return true | |
end | |
end | |
elseif it.name():find("enchant armour") then | |
body = items.equipped_at("Armour") | |
ac = armour_ac() | |
if body and not body.artefact and body.plus < ac | |
and body_armour_is_great(body) | |
and not body.name():find("quicksilver dragon armour") then | |
oldname = body.name() | |
if read2(it, letter(body)) then | |
say("ENCHANTING " .. oldname .. ".") | |
return true | |
end | |
end | |
for _,slotname in pairs(good_slots) do | |
if slotname ~= "Armour" then | |
it2 = items.equipped_at(slotname) | |
if it2 and not it2.artefact and it2.plus < 2 and it2.plus >= 0 | |
then | |
oldname = it2.name() | |
if read2(it, letter(it2)) then | |
say("ENCHANTING " .. oldname .. ".") | |
return true | |
end | |
end | |
if slotname == "Boots" and it2 and it2:name():find("barding") | |
and not it2.artefact and it2.plus < 4 and it2.plus >= 0 then | |
oldname = it2.name() | |
if read2(it, letter(it2)) then | |
say("ENCHANTING " .. oldname .. ".") | |
return true | |
end | |
end | |
end | |
end | |
if body and not body.artefact and body.plus < ac | |
and body_armour_is_good(body) | |
and not body.name():find("quicksilver dragon armour") then | |
oldname = body.name() | |
if read2(it, letter(body)) then | |
say("ENCHANTING " .. oldname .. ".") | |
return true | |
end | |
end | |
elseif it.name():find("recharging") then | |
for it2 in inventory() do | |
if it2.class(true) == "wand" and | |
(it2.name():find("heal wounds") and you.race() ~= "Vine Stalker" or | |
-- it2.name():find("hasting") or | |
it2.name():find("teleportation")) | |
and (it2.name():find("empty") or | |
it2.plus and it2.plus < 4) then | |
oldname = it2.name() | |
if read2(it, letter(it2)) then | |
say("RECHARGING " .. oldname .. ".") | |
return true | |
end | |
end | |
end | |
elseif it.name():find("remove curse") then | |
for it2 in inventory() do | |
if it2.cursed and it2.equipped then | |
return read(it) | |
end | |
end | |
end | |
elseif it.class(true) == "potion" then | |
if it.name():find("beneficial") and you.race() ~= "Ghoul" | |
or it.name():find("experience") then | |
return drink(it) | |
end | |
if it.name():find("cure mutation") then | |
if you.mutation("slow healing") > 0 and you.race() ~= "Deep Dwarf" | |
and you.race() ~= "Ghoul" or | |
you.mutation("dopey") > 1 or | |
intrinsic_want_dex() and you.mutation("clumsy") > 1 or | |
you.mutation("teleportitis") > 0 or | |
you.mutation("deformed body") > 0 and you.race() ~= "Naga" | |
and you.race() ~= "Centaur" or | |
you.mutation("berserk") > 0 or | |
you.mutation("deterioration") > 1 or | |
you.mutation("blurry vision") > 0 or | |
you.mutation("frail") > 0 or | |
you.mutation("forlorn") > 0 or | |
you.mutation("no device heal") > 0 and you.race() ~= "Vine Stalker" or | |
you.mutation("MP-powered wands") > 0 | |
then | |
return drink(it) | |
end | |
end | |
end | |
end | |
return false | |
end | |
function plan_zap_id() | |
if not can_zap() then | |
return false | |
end | |
for it in inventory() do | |
if it.class(true) == "wand" and | |
not (it.name():find("wand of") or it.name():find("empty")) then | |
l = items.index_to_letter(it.slot) | |
say("ZAPPING " .. it.name() .. ".") | |
magic("V" .. l .. "YY") | |
return true | |
end | |
end | |
return false | |
end | |
function plan_wield_weapon() | |
if items.equipped_at("Weapon") or you.berserk() or transformed() then | |
return false | |
end | |
if wskill() == "Unarmed Combat" then | |
return false | |
end | |
for it in inventory() do | |
if it and it.class(true) == "weapon" then | |
if equip_value(it, it:name()) >= 0 and not it:name():find("vamp") then | |
l = items.index_to_letter(it.slot) | |
say("Wielding weapon " .. it:name() .. ".") | |
magic("w" .. l .. "YY") | |
-- this might have a 0-turn fail because of unIDed vamp/holy | |
return nil | |
end | |
end | |
end | |
return false | |
end | |
function plan_upgrade_weapon() | |
if you.race() == "Troll" then | |
return false | |
end | |
twohands = true | |
if items.equipped_at("Shield") and you.race() ~= "Formicid" then | |
twohands = false | |
end | |
it_old = items.equipped_at("Weapon") | |
swappable = can_swap("Weapon") | |
for it in inventory() do | |
if it and it.class(true) == "weapon" and not it.equipped then | |
local equip = false | |
local drop = false | |
local new_value = equip_value(it, it:name()) | |
if new_value < 0 then | |
drop = true | |
elseif not it_old then | |
equip = true | |
elseif new_value > equip_value(it_old, it_old:name()) then | |
equip = true | |
else | |
drop = true | |
end | |
if equip and swappable and (twohands or it.hands < 2) then | |
if it.name():find("vamp") and you.race() ~= "Vampire" and not | |
(you.hunger_name() == "full" or you.hunger_name() == "very full" or | |
you.hunger_name() == "completely stuffed") then | |
say("Eating in order to wield vampiric weapon.") | |
if eat_permafood() then | |
return true | |
end | |
else | |
l = items.index_to_letter(it.slot) | |
say("UPGRADING to " .. it:name() .. " (value " .. new_value .. ").") | |
magic("w" .. l .. "YY") | |
-- this might have a 0-turn fail because of unIDed vamp/holy | |
return nil | |
end | |
end | |
if drop then | |
l = items.index_to_letter(it.slot) | |
say("DROPPING " .. it:name() .. " (value " .. new_value .. ").") | |
magic("d" .. l .. "\r") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_remove_terrible_jewellery() | |
if you.berserk() or transformed() then | |
return false | |
end | |
for it in inventory() do | |
if it and it.equipped and it.class(true) == "jewellery" | |
and not it.cursed | |
and equip_value(it, it:name()) < 0 then | |
say("REMOVING " .. it:name() .. ".") | |
magic("P" .. letter(it) .. "YY") | |
return true | |
end | |
end | |
return false | |
end | |
function plan_upgrade_amulet() | |
it_old = items.equipped_at("Amulet") | |
swappable = can_swap("Amulet") | |
for it in inventory() do | |
if it and it.class(true) == "jewellery" and it:name():find("amulet") | |
and (it.fully_identified or count_item("scroll", "remove curse") > 1) | |
and not it.equipped then | |
local equip = false | |
local drop = false | |
local new_value = equip_value(it, it:name()) | |
local new_value_with_resists = equip_value(it, it:name(), true) | |
if new_value < 0 then | |
drop = true | |
elseif not it_old then | |
if new_value_with_resists >= 0 then | |
equip = true | |
end | |
elseif new_value_with_resists > equip_value(it_old, it_old:name()) then | |
equip = true | |
elseif new_value <= equip_value(it_old, it_old:name()) then | |
drop = true | |
end | |
if equip and swappable then | |
l = items.index_to_letter(it.slot) | |
say("UPGRADING to " .. it:name() .. " (value " .. | |
new_value_with_resists .. ").") | |
magic("P" .. l .. "YY") | |
return true | |
end | |
if drop then | |
l = items.index_to_letter(it.slot) | |
say("DROPPING " .. it:name() .. " (value " .. new_value .. ").") | |
magic("d" .. l .. "\r") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_upgrade_rings() | |
local it_rings = ring_list() | |
local worst_ring | |
local worst_ring_value | |
local empty = (empty_ring_slots() > 0) | |
if not empty then | |
local r | |
for _, r in ipairs(it_rings) do | |
local val = equip_value(r, r:name()) | |
if (not worst_ring) or val < worst_ring_value then | |
worst_ring = r | |
worst_ring_value = val | |
end | |
end | |
end | |
if worst_ring and worst_ring.cursed then | |
return false | |
end | |
for it in inventory() do | |
if it and it.class(true) == "jewellery" and it:name():find("ring") | |
and (it.fully_identified or count_item("scroll", "remove curse") > 1) | |
and not it.equipped then | |
local equip = false | |
local drop = false | |
local swap = nil | |
local new_value = equip_value(it, it:name()) | |
local new_value_with_resists = equip_value(it, it:name(), true) | |
if new_value < 0 then | |
drop = true | |
elseif empty then | |
if new_value_with_resists >= 0 then | |
equip = true | |
end | |
elseif new_value_with_resists > worst_ring_value then | |
equip = true | |
swap = worst_ring.slot | |
else | |
drop = true | |
end | |
if equip then | |
l = items.index_to_letter(it.slot) | |
say("UPGRADING to " .. it:name() .. " (value " .. | |
new_value_with_resists .. ").") | |
if swap then | |
items.swap_slots(swap, items.letter_to_index('Y'), false) | |
if l == 'Y' then | |
l = items.index_to_letter(swap) | |
end | |
end | |
magic("P" .. l .. "YY") | |
return true | |
end | |
if drop then | |
l = items.index_to_letter(it.slot) | |
say("DROPPING " .. it:name() .. " (value " .. new_value .. ").") | |
magic("d" .. l .. "\r") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_maybe_upgrade_armour() | |
if not upgrade_phase then | |
return false | |
end | |
return plan_upgrade_armour() | |
end | |
function plan_upgrade_armour() | |
for it in inventory() do | |
if it and it.class(true) == "armour" and not it.equipped then | |
local st, _ = it.subtype() | |
local equip = false | |
local drop = false | |
local swappable | |
it_old = items.equipped_at(good_slots[st]) | |
swappable = can_swap(good_slots[st]) | |
local new_value = equip_value(it, it:name()) | |
local new_value_with_resists = equip_value(it, it:name(), true) | |
if new_value < 0 then | |
drop = true | |
elseif not it_old then | |
if new_value_with_resists >= 0 then | |
equip = true | |
end | |
elseif new_value_with_resists > equip_value(it_old, it_old:name()) then | |
equip = true | |
elseif new_value <= equip_value(it_old, it_old:name()) then | |
drop = true | |
end | |
if it:name():find("helmet") and (you.mutation("horns") > 0 | |
or you.mutation("beak") > 0 or you.mutation("antennae") > 0) then | |
equip = false | |
drop = true | |
end | |
if good_slots[st] == "Helmet" and | |
(you.mutation("horns") >= 3 or you.mutation("antennae") >= 3) then | |
equip = false | |
drop = true | |
end | |
if it:name():find("boots") and | |
(you.mutation("talons") >= 3 or you.mutation("hooves") >= 3) then | |
equip = false | |
drop = true | |
end | |
if it:name():find("boots") and you.race() == "Merfolk" | |
and (view.feature_at(0,0) == "shallow_water" or | |
view.feature_at(0,0) == "deep_water") then | |
equip = false | |
drop = false | |
end | |
if good_slots[st] == "Gloves" and you.mutation("claws") >= 3 then | |
equip = false | |
drop = true | |
end | |
if equip and swappable then | |
l = items.index_to_letter(it.slot) | |
say("UPGRADING to " .. it:name() .. " (value " .. | |
new_value_with_resists .. ").") | |
magic("W" .. l .. "YN") | |
upgrade_phase = true | |
return true | |
end | |
if drop then | |
l = items.index_to_letter(it.slot) | |
say("DROPPING " .. it:name() .. " (value " .. new_value .. ").") | |
magic("d" .. l .. "\r") | |
return true | |
end | |
end | |
end | |
for it in inventory() do | |
if it and it.equipped and it.class(true) == "armour" and (not it.cursed) | |
and equip_value(it, it:name()) < 0 then | |
l = items.index_to_letter(it.slot) | |
say("REMOVING " .. it:name() .. ".") | |
magic("T" .. l .. "YN") | |
return true | |
end | |
end | |
return false | |
end | |
function plan_remove_redundant_jewels() | |
local it_rings = ring_list() | |
for _, it in ipairs(it_rings) do | |
if it:name():find("see invisible") and not it.cursed then | |
it2 = items.equipped_at("Helmet") | |
if it2 and it2:name():find("see invisible") then | |
say("Removing redundant " .. it:name() .. ".") | |
magic("P" .. items.index_to_letter(it.slot) .. "YY") | |
return true | |
end | |
for _,slotname in pairs(good_slots) do | |
it2 = items.equipped_at(slotname) | |
if it2 and it2:name():find("SInv") then | |
say("Removing redundant " .. it:name() .. ".") | |
magic("P" .. items.index_to_letter(it.slot) .. "YY") | |
return true | |
end | |
end | |
end | |
if it:name():find("poison resistance") and not it.cursed then | |
for _,slotname in pairs(good_slots) do | |
it2 = items.equipped_at(slotname) | |
if it2 and (it2.artprops and it2.artprops["rPois"] | |
and it2.artprops["rPois"] > 0 or | |
it2.ego() == "poison resistance") then | |
say("Removing redundant " .. it:name() .. ".") | |
magic("P" .. items.index_to_letter(it.slot) .. "YY") | |
return true | |
end | |
end | |
end | |
end | |
return false | |
end | |
function plan_go_up() | |
local feat = view.feature_at(0,0) | |
if feat:find("stone_stairs_up") or feat == "escape_hatch_up" | |
or feat == "exit_zot" or feat == "exit_dungeon" | |
or feat == "exit_depths" then | |
expect_new_location = true | |
magic("<") | |
return true | |
end | |
return false | |
end | |
function plan_go_down() | |
local feat = view.feature_at(0,0) | |
if feat:find("stone_stairs_down") then | |
expect_new_location = true | |
magic(">") | |
return true | |
end | |
return false | |
end | |
function want_to_stairdace_up() | |
local n = stairdance_count[where] or 0 | |
if n > 10 then | |
return false | |
end | |
if you.caught() or you.mesmerised() or you.confused() or you.constricted() or | |
you.rooted() or you.transform() == "tree" or you.transform() == "fungus" then | |
return false | |
end | |
local feat = view.feature_at(0,0) | |
if feat:find("stone_stairs_up") then | |
local x, y, m | |
for x = -1, 1 do | |
for y = -1, 1 do | |
m = monster.get_monster_at(x, y) | |
-- who can use stairs? let's suppose anyone except undead and plants | |
if m and supdist(x,y) > 0 and | |
m:holiness() ~= 2 and m:holiness() ~= 5 and | |
m:attitude() < ATT_NEUTRAL and | |
m:stabbability() < 1 then | |
stairdance_count[where] = n + 1 | |
return true | |
end | |
end | |
end | |
else | |
return false | |
end | |
end | |
function plan_stairdance_up() | |
if want_to_stairdace_up() then | |
expect_new_location = true | |
say("STAIRDANCE") | |
magic("<") | |
return true | |
end | |
return false | |
end | |
function plan_simple_go_down() | |
if travel_destination then | |
return false | |
end | |
if found_branch("L") and not | |
util.contains(c_persist.branches_entered, "L") then | |
return false | |
end | |
if where == "Orc:3" then | |
return false | |
end | |
if where == "Vaults:4" and you.num_runes() < 2 then | |
return false | |
end | |
if in_branch("A") and you.have_rune("barnacled") then | |
return false | |
end | |
expect_new_location = true | |
magic("G>") | |
return true | |
end | |
function want_altar() | |
return (you.god() ~= "Trog" and you.race() ~= "Demigod") | |
end | |
function plan_go_to_temple() | |
local c = c_persist.plan_fail_count["try_go_to_temple"] | |
if c and c >= 10 then | |
return false | |
end | |
if found_branch("T") and want_altar() and not | |
util.contains(c_persist.branches_entered, "T") and in_branch("D") then | |
expect_new_location = true | |
magic("GTY") | |
return true | |
end | |
return false | |
end | |
function plan_go_to_lair() | |
if found_branch("L") and not | |
util.contains(c_persist.branches_entered, "T") and in_branch("D") then | |
expect_new_location = true | |
magic("GL\rY") | |
return true | |
end | |
return false | |
end | |
function plan_enter_branch() | |
local br | |
if found_branch("L") and not | |
util.contains(c_persist.branches_entered, "L") and in_branch("D") then | |
br = "L" | |
elseif found_branch("O") and not | |
util.contains(c_persist.branches_entered, "O") and in_branch("D") and | |
util.contains(c_persist.branches_entered, "L") then | |
br = "O" | |
end | |
if br then | |
expect_new_location = true | |
magic("G" .. br .. "\rY") | |
return true | |
end | |
return false | |
end | |
function plan_go_to_portal_entrance() | |
for _, por in ipairs(c_persist.portals_found) do | |
for _, val in ipairs(portal_data) do | |
if val[1] == por then | |
magic(control('f') .. "@" .. val[2] .. "\ra\r") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_go_to_zig() | |
if ZIG_DIVE <= 0 then | |
return false | |
end | |
if you.num_runes() < 3 then | |
return false | |
end | |
magic(control('f') .. "gateway to a ziggurat" .. "\ra\r") | |
return true | |
end | |
function plan_go_to_portal_exit() | |
if in_portal() then | |
magic("X<\r") | |
return true | |
end | |
return false | |
end | |
function plan_go_to_abyss_portal() | |
if not want_to_stay_in_abyss() or not where:find("Depths") then | |
return false | |
else | |
expect_new_location = true | |
magic(control('f') .. "one-way gate to the infinite horrors of the Abyss" .. "\ra\r") | |
return true | |
end | |
end | |
function plan_go_to_abyss_downstairs() | |
if want_to_stay_in_abyss() and where == "Abyss:1" or where == "Abyss:2" then | |
magic("X>\r") | |
return true | |
end | |
return false | |
end | |
function plan_go_to_abyss_exit() | |
magic("X<\r") | |
return true | |
end | |
function plan_enter_zig() | |
if ZIG_DIVE <= 0 then | |
return false | |
end | |
if you.num_runes() < 3 then | |
return false | |
end | |
if view.feature_at(0,0) == "enter_ziggurat" then | |
expect_new_location = true | |
magic(">") | |
return true | |
end | |
return false | |
end | |
function plan_enter_portal() | |
for _, por in ipairs(c_persist.portals_found) do | |
if string.find(view.feature_at(0,0), "enter_" .. get_feat_name(por)) then | |
expect_portal = true | |
expect_new_location = true | |
magic(">") | |
return true | |
end | |
return false | |
end | |
return false | |
end | |
function plan_exit_portal() | |
if not in_portal() then | |
return false | |
end | |
if string.find(view.feature_at(0,0), "exit_" .. get_feat_name(where)) then | |
expect_new_location = true | |
magic("<") | |
return true | |
end | |
return false | |
end | |
function plan_enter_abyss() | |
if view.feature_at(0,0) == "enter_abyss" and want_to_stay_in_abyss() then | |
expect_new_location = true | |
magic(">Y") | |
return true | |
end | |
return false | |
end | |
function plan_go_down_abyss() | |
if view.feature_at(0,0) == "abyssal_stair" and want_to_stay_in_abyss() then | |
expect_new_location = true | |
magic(">") | |
return true | |
end | |
return false | |
end | |
function plan_zig_leave_level() | |
if not where:find("Zig") then | |
return false | |
end | |
if where:find(tostring(ZIG_DIVE)) then | |
if view.feature_at(0,0) == "exit_ziggurat" then | |
magic("<Y") | |
return true | |
end | |
elseif string.find(view.feature_at(0,0), "stone_stairs_down") then | |
magic(">") | |
return true | |
end | |
return false | |
end | |
function plan_exit_abyss() | |
if view.feature_at(0,0) == "exit_abyss" then | |
expect_new_location = true | |
magic("<") | |
return true | |
end | |
return false | |
end | |
function plan_step_towards_lair() | |
local x, y | |
if stepped_on_lair or not found_branch("L") then | |
return false | |
end | |
for x = -8,8 do | |
for y = -8,8 do | |
if view.feature_at(x,y) == "enter_lair" | |
and you.see_cell_no_trans(x,y) then | |
if x == 0 and y == 0 then | |
stepped_on_lair = true | |
return false | |
else | |
kill_plant_mode = true | |
local result = move_towards(x,y) | |
kill_plant_mode = false | |
return result | |
end | |
end | |
end | |
end | |
return false | |
end | |
function plan_continue_travel() | |
if travel_destination then | |
if in_branch(travel_destination) or | |
not found_branch(travel_destination) then | |
travel_destination = nil | |
return false | |
end | |
expect_new_location = true | |
magic("G" .. travel_destination .. "\rY") | |
return true | |
end | |
return false | |
end | |
function choose_lair_rune_branch() | |
if crawl.random2(2) == 0 then | |
branch_options = { "P", "N", "S", "A" } | |
else | |
branch_options = { "S", "A", "P", "N" } | |
end | |
if WATER_RUNE_SECOND then | |
branch_options = { "P", "N", "S", "A" } | |
end | |
for _, branch_code in ipairs(branch_options) do | |
if found_branch(branch_code) and | |
not util.contains(c_persist.branches_entered, branch_code) then | |
return branch_code | |
end | |
end | |
--tries = 0 | |
--while tries < 100 do | |
-- i = 1 + crawl.random2(4) | |
-- branch_code = branch_options[i] | |
-- if found_branch(branch_code) and | |
-- not util.contains(c_persist.branches_entered, branch_code) then | |
-- return branch_code | |
-- end | |
-- tries = tries + 1 | |
--end | |
return nil | |
end | |
function plan_new_travel() | |
local back_to_D_places = { "Temple", "Orc:3", "Orc:4", "Vaults:4"} | |
if util.contains(back_to_D_places, where) then | |
travel_destination = "D" | |
end | |
if where == "Lair:8" then | |
if travel.find_deepest_explored("D") == 15 and you.num_runes() < 2 then | |
travel_destination = choose_lair_rune_branch() | |
else | |
travel_destination = "D" | |
end | |
end | |
if where == "Snake:5" and you.have_rune("serpentine") then | |
travel_destination = "D" | |
end | |
if where == "Swamp:5" and you.have_rune("decaying") then | |
travel_destination = "D" | |
end | |
if where == "Spider:5" and you.have_rune("gossamer") then | |
travel_destination = "D" | |
end | |
if where == "Shoals:5" and you.have_rune("barnacled") then | |
travel_destination = "D" | |
end | |
if where == "Vaults:5" and you.have_rune("silver") then | |
travel_destination = "D" | |
end | |
if where == "D:15" then | |
if you.num_runes() == 1 and not util.contains(c_persist.branches_entered, "V") | |
or you.num_runes() == 2 and util.contains(c_persist.branches_entered, "U") then | |
travel_destination = "V" | |
elseif you.num_runes() >= 1 and not util.contains(c_persist.branches_entered, "U") and not (EARLY_SECOND_RUNE and you.num_runes() == 1) | |
or you.num_runes() >= 3 then | |
travel_destination = "U" | |
else | |
travel_destination = "L" | |
end | |
end | |
if where == "Depths:5" then | |
if you.num_runes() >= 3 then | |
travel_destination = "Z" | |
else | |
travel_destination = "D" | |
end | |
end | |
return plan_continue_travel() | |
end | |
function plan_fly() | |
if you.xl() >= 14 and intrinsic_flight() and not you.flying() then | |
local a,b = you.mp() | |
if a >= 3 and not you.rooted() and you.hunger_name() ~= "starving" then | |
if use_ability("Fly") then | |
say("FLYING.") | |
return true | |
end | |
end | |
end | |
return false | |
end | |
function plan_find_upstairs() | |
magic("X<\r") | |
return true | |
end | |
function plan_gd1() | |
expect_new_location = true | |
magic("GD1\rY") | |
return true | |
end | |
function plan_zig_go_to_stairs() | |
if not where:find("Zig") then | |
return false | |
end | |
if where:find(tostring(ZIG_DIVE)) then | |
magic("X<\r") | |
else | |
magic("X>\r") | |
end | |
return true | |
end | |
function plan_find_downstairs() | |
-- try to avoid branch entrances by going to a random > from them | |
local feat = view.feature_at(0,0) | |
if feat:find("enter_") or feat == "escape_hatch_down" then | |
local i,j | |
local c = "X" | |
j = crawl.roll_dice(1,12) | |
for i = 1,j do | |
c = (c .. ">") | |
end | |
magic(c .. "\r") | |
return true | |
end | |
magic("X>\r") | |
return true | |
end | |
function plan_stuck() | |
if you.hunger_name() == "starving" then | |
return random_step("starving") | |
end | |
stuck_turns = stuck_turns + 1 | |
if stuck_turns > 5000 then | |
magic(control('q') .. "yes\r") | |
return true | |
end | |
return random_step("stuck") | |
-- panic("Stuck!") | |
end | |
function plan_unshaft() | |
if where_shafted_from and (where_shafted_from ~= you.where()) then | |
--say("Trying to unshaft to " .. where_shafted_from .. ".") | |
expect_new_location = true | |
magic("G<") | |
return true | |
end | |
return false | |
end | |
function plan_not_coded() | |
panic("Need to code this!") | |
return true | |
end | |
function random_step(reason) | |
if you.mesmerised() then | |
say("Waiting to end mesmerise (" .. reason .. ").") | |
magic("s") | |
return true | |
end | |
local i,j | |
local dx,dy | |
local count = 0 | |
for i = -1,1 do | |
for j = -1,1 do | |
if not (i == 0 and j == 0) and is_traversable(i,j) | |
and not view.withheld(i,j) | |
and not monster_in_way(i,j) then | |
count = count + 1 | |
if crawl.one_chance_in(count) then | |
dx = i | |
dy = j | |
end | |
end | |
end | |
end | |
if count > 0 then | |
say("Stepping randomly (" .. reason .. ").") | |
magic(delta_to_vi(dx,dy) .. "YY") | |
return true | |
else | |
say("Standing still (" .. reason .. ").") | |
magic("s") | |
return true | |
end | |
-- return false | |
end | |
function plan_disturbance_random_step() | |
if crawl.messages(5):find("There is a strange disturbance nearby!") then | |
return random_step("disturbance") | |
end | |
return false | |
end | |
function plan_wait() | |
rest() | |
return true | |
end | |
function plan_flail_at_invis() | |
if options.autopick_on then | |
invisi_count = 0 | |
invisi_sigmund = false | |
return false | |
end | |
if invisi_count > 100 then | |
say("Invisible monster not found???") | |
invisi_count = 0 | |
invisi_sigmund = false | |
magic(control('a')) | |
return true | |
end | |
invisi_count = invisi_count + 1 | |
local x,y | |
if invisi_sigmund and (sigmund_dx ~= 0 or sigmund_dy ~= 0) then | |
x = sigmund_dx | |
y = sigmund_dy | |
if adjacent(x,y) and is_traversable(x,y) then | |
magic(control(delta_to_vi(x,y))) | |
return true | |
elseif x == 0 and is_traversable(0,sign(y)) then | |
magic(delta_to_vi(0,sign(y))) | |
return true | |
elseif y == 0 and is_traversable(sign(x),0) then | |
magic(delta_to_vi(sign(x),0)) | |
return true | |
end | |
end | |
local success = false | |
local tries = 0 | |
while not success and tries < 100 do | |
x = -1 + crawl.random2(3) | |
y = -1 + crawl.random2(3) | |
tries = tries + 1 | |
if (x ~= 0 or y ~= 0) and is_traversable(x,y) | |
and view.feature_at(x,y) ~= "closed_door" | |
and view.feature_at(x,y) ~= "runed_door" then | |
success = true | |
end | |
end | |
if tries >= 100 then | |
magic("s") | |
else | |
magic(control(delta_to_vi(x,y))) | |
end | |
return true | |
end | |
function plan_cure_confusion() | |
if you.confused() and (danger or not options.autopick_on) then | |
if view.cloud_at(0,0) == "noxious fumes" and not meph_immune() then | |
return false | |
end | |
if drink_by_name("curing") then | |
say("(to cure confusion)") | |
return true | |
end | |
end | |
return false | |
end | |
function plan_cure_statzero() | |
local str, mstr = you.strength() | |
local int, mint = you.intelligence() | |
local dex, mdex = you.dexterity() | |
if str <= 0 and mstr > 0 or int <= 0 and mint > 0 or dex <= 0 and mdex > 0 | |
then | |
return drink_by_name("restore abilities") | |
end | |
return false | |
end | |
function plan_cure_bad_statdrain() | |
if you.berserk() or transformed() then | |
return false | |
end | |
local str, mstr = you.strength() | |
local int, mint = you.intelligence() | |
local dex, mdex = you.dexterity() | |
if str <= mstr - 3 or str <= 3 and str < mstr or | |
int <= mint - 3 or int <= 3 and int < mint or | |
dex <= mint - 3 or dex <= 3 and dex < mdex then | |
return drink_by_name("restore abilities") | |
end | |
return false | |
end | |
function plan_teleport() | |
if can_teleport() and want_to_teleport() then | |
-- return false | |
return teleport() | |
end | |
return false | |
end | |
function plan_orbrun_teleport() | |
if can_teleport() and want_to_orbrun_teleport() then | |
return teleport() | |
end | |
return false | |
end | |
function plan_orbrun_holy_word() | |
if can_read() and want_to_orbrun_holy_word() then | |
return holy_word() | |
end | |
return false | |
end | |
function plan_swamp_clear_exclusions() | |
if where ~= "Swamp:5" then | |
return false | |
end | |
magic("X" .. control('e')) | |
return true | |
end | |
function plan_swamp_go_to_rune() | |
if where ~= "Swamp:5" or you.have_rune("decaying") then | |
return false | |
end | |
magic(control('f') .. "@decaying rune" .. "\ra\r") | |
return true | |
end | |
function plan_swamp_clouds_hack() | |
if where ~= "Swamp:5" then | |
return false | |
end | |
if you.have_rune("decaying") and can_teleport() and teleport() then | |
return true | |
end | |
local x,y | |
local bestx,besty | |
local dist | |
local bestdist = 11 | |
for x = -1,1 do | |
for y = -1,1 do | |
if supdist(x,y) > 0 and view.is_safe_square(x,y) | |
and not view.withheld(x,y) and not monster_in_way(x,y) then | |
dist = 11 | |
for x2 = -8,8 do | |
for y2 = -8,8 do | |
if (view.cloud_at(x2,y2) == "freezing vapour" | |
or view.cloud_at(x2,y2) == "foul pestilence") | |
and you.see_cell_no_trans(x2,y2) then | |
if supdist(x-x2,y-y2) < dist then | |
dist = supdist(x-x2,y-y2) | |
end | |
end | |
end | |
end | |
if dist < bestdist then | |
bestx = x | |
besty = y | |
bestdist = dist | |
end | |
end | |
end | |
end | |
if bestdist < 11 then | |
magic(delta_to_vi(bestx,besty) .. "Y") | |
return true | |
end | |
for x = -8,8 do | |
for y = -8,8 do | |
if (view.cloud_at(x,y) == "freezing vapour" | |
or view.cloud_at(x,y) == "foul pestilence") | |
and you.see_cell_no_trans(x,y) then | |
return random_step("Swamp:5") | |
end | |
end | |
end | |
return plan_stuck_teleport() | |
end | |
function plan_stuck_teleport() | |
if can_teleport() then | |
return teleport() | |
end | |
return false | |
end | |
function read(c) | |
if not can_read() then | |
return false | |
end | |
say("READING " .. item(c).name() .. ".") | |
magic("r" .. letter(c)) | |
return true | |
end | |
function read2(c,etc) | |
if not can_read() then | |
return false | |
end | |
local int, mint = you.intelligence() | |
if int <= 0 then | |
-- failing to read a scroll due to intzero can make qw unhappy | |
return false | |
end | |
say("READING " .. item(c).name() .. ".") | |
magic("r" .. letter(c) .. etc) | |
return true | |
end | |
function drink(c) | |
if not can_drink() then | |
return false | |
end | |
say("DRINKING " .. item(c).name() .. ".") | |
magic("q" .. letter(c)) | |
return true | |
end | |
function selfzap(c) | |
if not can_zap() then | |
return false | |
end | |
say("ZAPPING " .. item(c).name() .. ".") | |
magic("V" .. letter(c) .. ".") | |
return true | |
end | |
function read_by_name(name) | |
local c = find_item("scroll", name) | |
if (c and read(c)) then | |
return true | |
end | |
return false | |
end | |
function drink_by_name(name) | |
local c = find_item("potion", name) | |
if (c and drink(c)) then | |
return true | |
end | |
return false | |
end | |
function selfzap_by_name(name) | |
local c = find_item("wand", name) | |
if (c and selfzap(c)) then | |
return true | |
end | |
return false | |
end | |
function teleport() | |
if selfzap_by_name("teleportation") then | |
dd_hw_meter = 0 | |
return true | |
end | |
if read_by_name("teleportation") then | |
dd_hw_meter = 0 | |
return true | |
end | |
return false | |
end | |
function holy_word() | |
return read_by_name("holy word") | |
end | |
function plan_cure_poison_rotting() | |
local hp, mhp = you.hp() | |
if you.poison_survival() <= 1 and you.poisoned() | |
or you.race() == "Deep Dwarf" and you.poison_survival() <= hp - 10 then | |
if drink_by_name("curing") then | |
say("(to cure poison)") | |
return true | |
end | |
end | |
if you.rotting() then | |
if drink_by_name("curing") then | |
say("(to cure rotting)") | |
return true | |
end | |
end | |
if you.poison_survival() <= 1 and you.poisoned() and can_hand() then | |
hand() | |
return true | |
end | |
return false | |
end | |
function move_towards(dx, dy) | |
if you.transform() == "tree" or you.transform() == "fungus" | |
or you.confused() and | |
(dangerous_terrain_adjacent() or count_bia(1) > 0) then | |
magic("s") | |
return true | |
end | |
local move = nil | |
if abs(dx) > abs(dy) then | |
if abs(dy) == 1 then move = try_move(sign(dx), 0) end | |
if move == nil then move = try_move(sign(dx), sign(dy)) end | |
if move == nil then move = try_move(sign(dx), 0) end | |
if move == nil and abs(dx) > abs(dy)+1 then | |
move = try_move(sign(dx), 1) end | |
if move == nil and abs(dx) > abs(dy)+1 then | |
move = try_move(sign(dx), -1) end | |
if move == nil then move = try_move(0, sign(dy)) end | |
elseif abs(dx) == abs(dy) then | |
move = try_move(sign(dx), sign(dy)) | |
if move == nil then move = try_move(sign(dx), 0) end | |
if move == nil then move = try_move(0, sign(dy)) end | |
else | |
if abs(dx) == 1 then move = try_move(0, sign(dy)) end | |
if move == nil then move = try_move(sign(dx), sign(dy)) end | |
if move == nil then move = try_move(0, sign(dy)) end | |
if move == nil and abs(dy) > abs(dx)+1 then | |
move = try_move(1, sign(dy)) end | |
if move == nil and abs(dy) > abs(dx)+1 then | |
move = try_move(-1, sign(dy)) end | |
if move == nil then move = try_move(sign(dx), 0) end | |
end | |
if move == nil or move_count >= 10 then | |
add_ignore(dx,dy) | |
table.insert(failed_move, 20*dx+dy) | |
return false | |
else | |
if (abs(dx) > 1 or abs(dy) > 1) and not kill_plant_mode | |
and view.feature_at(dx,dy) ~= "closed_door" then | |
did_move = true | |
if monster_array[dx][dy] or did_move_towards_monster > 0 then | |
local move_x, move_y = vi_to_delta(move) | |
target_memory_x = dx - move_x | |
target_memory_y = dy - move_y | |
did_move_towards_monster = 2 | |
end | |
end | |
magic(move .. "Y") | |
return true | |
end | |
end | |
function plan_continue_tab() | |
if did_move_towards_monster == 0 then | |
return false | |
end | |
if supdist(target_memory_x, target_memory_y) == 0 then | |
return false | |
end | |
if not options.autopick_on then | |
return false | |
end | |
return move_towards(target_memory_x, target_memory_y) | |
end | |
function add_ignore(dx,dy) | |
m = monster_array[dx][dy] | |
if not m then | |
return | |
end | |
name = m:name() | |
if not util.contains(ignore_list, name) then | |
table.insert(ignore_list, name) | |
crawl.setopt("runrest_ignore_monster ^= " .. name .. ":1") | |
--say("Ignoring " .. name .. ".") | |
end | |
end | |
function remove_ignore(dx,dy) | |
m = monster_array[dx][dy] | |
name = m:name() | |
for i,mname in ipairs(ignore_list) do | |
if mname == name then | |
table.remove(ignore_list, i) | |
crawl.setopt("runrest_ignore_monster -= " .. name .. ":1") | |
--say("Unignoring " .. name .. ".") | |
return | |
end | |
end | |
end | |
function clear_ignores() | |
local size = #ignore_list | |
local mname | |
local i | |
if size > 0 then | |
for i = 1, size do | |
mname = table.remove(ignore_list) | |
crawl.setopt("runrest_ignore_monster -= " .. mname .. ":1") | |
--say("Unignoring " .. mname .. ".") | |
end | |
end | |
end | |
-- this gets stuck if netted, confused, etc | |
function attack_reach(x, y) | |
magic('vr' .. vector_move(x, y) .. '.') | |
end | |
function attack_melee(x, y) | |
if you.confused() then | |
if count_bia(1) > 0 then | |
magic("s") | |
return | |
elseif dangerous_terrain_adjacent() or you.transform() == "tree" then | |
magic(control(delta_to_vi(x, y)) .. "Y") | |
return | |
end | |
end | |
magic(delta_to_vi(x, y) .. "Y") | |
end | |
function make_attack(x, y, info) | |
if info.attack_type == 2 then attack_melee(x, y) | |
elseif info.attack_type == 1 then attack_reach(x, y) | |
else | |
return move_towards(x, y) | |
end | |
return true | |
end | |
function use_ability(name, extra) | |
for letter, abil in pairs(you.ability_table()) do | |
if abil == name then | |
say("INVOKING " .. name .. ".") | |
magic("a" .. letter .. (extra or "")) | |
return true | |
end | |
end | |
end | |
function note(x) | |
crawl.take_note(you.turns() .. " ||| " .. x) | |
end | |
function say(x) | |
crawl.mpr(you.turns() .. " ||| " .. x) | |
note(x) | |
end | |
-- these few functions are called directly from ready() | |
function record_portal_found(por) | |
if not util.contains(c_persist.portals_found, por) then | |
say("Found " .. por .. ".") | |
table.insert(c_persist.portals_found, por) | |
end | |
end | |
function check_messages() | |
local recent_messages = crawl.messages(20) | |
local very_recent_messages = crawl.messages(5) | |
if very_recent_messages:find("Sigmund flickers and vanishes") then | |
invisi_sigmund = true | |
end | |
if very_recent_messages:find("Your surroundings suddenly seem different") then | |
invisi_sigmund = false | |
end | |
str1 = "Your pager goes off" | |
str2 = "qwqwqw" | |
if recent_messages:find(str1) then | |
a = recent_messages:reverse():find(str1:reverse()) | |
b = recent_messages:reverse():find(str2:reverse()) | |
if (not b) or a < b then | |
have_message = true | |
end | |
end | |
if in_portal() then | |
return false | |
end | |
if recent_messages:find("Found") then | |
for _, value in ipairs(portal_data) do | |
if recent_messages:find(value[2]) then | |
record_portal_found(value[1]) | |
end | |
end | |
end | |
end | |
function plan_message() | |
if read_message then | |
crawl.setopt("clear_messages = false") | |
magic("_") | |
read_message = false | |
else | |
crawl.setopt("clear_messages = true") | |
magic(":qwqwqw\r") | |
read_message = true | |
have_message = false | |
crawl.delay(2500) | |
end | |
end | |
---------------------------------------- | |
-- cascading plans: this is the bot's flowchart for using the above plans | |
function cascade(plans) | |
local plan_turns = {} | |
local plan_result = {} | |
return function () | |
for i, plandata in ipairs(plans) do | |
plan = plandata[1] | |
if you.turns() ~= plan_turns[plan] or plan_result[plan] == nil then | |
--say(plandata[2]) | |
result = plan() | |
if not automatic then | |
return true | |
end | |
plan_turns[plan] = you.turns() | |
plan_result[plan] = result | |
if result == nil or result == true then | |
if DELAYED and result == true then | |
crawl.delay(next_delay) | |
end | |
next_delay = DELAY_TIME | |
return nil | |
end | |
elseif plan_turns[plan] and plan_result[plan] == true then | |
if not plandata[2]:find("^try") then | |
panic(plandata[2] .. " failed despite returning true.") | |
end | |
if not c_persist.plan_fail_count[plandata[2]] then | |
c_persist.plan_fail_count[plandata[2]] = 0 | |
end | |
c_persist.plan_fail_count[plandata[2]] = c_persist.plan_fail_count[plandata[2]] + 1 | |
end | |
end | |
return false | |
end | |
end | |
-- any plan that might not know whether or not it successfully took an action | |
-- (e.g. autoexplore) should prepend "try_" to its text | |
plan_pre_explore = cascade { | |
{plan_fly, "fly"}, | |
{plan_upgrade_weapon, "upgrade_weapon"}, | |
{plan_maybe_upgrade_armour, "maybe_upgrade_armour"}, | |
{plan_use_good_consumables, "use_good_consumables"}, | |
} -- hack | |
plan_pre_explore2 = cascade { | |
{plan_disturbance_random_step, "disturbance_random_step"}, | |
{plan_remove_redundant_jewels, "remove_redundant_jewels"}, | |
{plan_upgrade_armour, "upgrade_armour"}, | |
{plan_upgrade_amulet, "upgrade_amulet"}, | |
{plan_upgrade_rings, "upgrade_rings"}, | |
{plan_read_id, "read_id"}, | |
{plan_zap_id, "zap_id"}, | |
{plan_quaff_id, "quaff_id"}, | |
{plan_use_id_scrolls, "use_id_scrolls"}, | |
{plan_drop_filtered_items, "try_drop_filtered_items"}, | |
} -- hack | |
plan_emergency = cascade { | |
{plan_cure_statzero, "cure_statzero"}, | |
{plan_cure_confusion, "cure_confusion"}, | |
{plan_remove_terrible_jewellery, "remove_terrible_jewellery"}, | |
{plan_teleport, "teleport"}, | |
{plan_dd_recharge_teleport, "dd_recharge_teleport"}, | |
{plan_cure_bad_poison, "cure_bad_poison"}, | |
{plan_heal_wounds, "heal_wounds"}, | |
{plan_choose_tactical_step, "choose_tactical_step"}, | |
{plan_cloud_step, "cloud_step"}, | |
{plan_hand, "hand"}, | |
{plan_resistance, "resistance"}, | |
{plan_bia, "bia"}, | |
{plan_dd_recharge_heal_wounds, "dd_recharge_heal_wounds"}, | |
{plan_wield_weapon, "wield_weapon"}, | |
{plan_water_step, "water_step"}, | |
{plan_berserk, "berserk"}, | |
{plan_other_step, "other_step"}, | |
} -- hack | |
plan_orbrun_emergency = cascade { | |
{plan_cure_statzero, "cure_statzero"}, | |
{plan_cure_confusion, "cure_confusion"}, | |
{plan_orbrun_teleport, "orbrun_teleport"}, | |
{plan_dd_recharge_teleport, "dd_recharge_teleport"}, | |
{plan_orbrun_holy_word, "orbrun_holy_word"}, | |
{plan_orbrun_heal_wounds, "orbrun_heal_wounds"}, | |
{plan_orbrun_haste, "orbrun_haste"}, | |
{plan_dd_orbrun_recharge_hasting, "orbrun_dd_recharge_hasting"}, | |
{plan_hand, "hand"}, | |
{plan_resistance, "resistance"}, | |
{plan_dd_recharge_heal_wounds, "dd_recharge_heal_wounds"}, | |
{plan_wield_weapon, "wield_weapon"}, | |
{plan_orbrun_might, "orbrun_might"}, | |
} -- hack | |
plan_eatrest = cascade { | |
{plan_eat_chunk, "eat_chunk"}, | |
{plan_eat_permafood, "eat_permafood"}, | |
{plan_dd_hand_for_healing, "dd_hand_for_healing"}, | |
{plan_rest, "rest"}, | |
{plan_handle_corpses, "handle_corpses"}, | |
{plan_find_corpses, "try_find_corpses"}, | |
{plan_eat_anyway, "eat_anyway"}, | |
} -- hack | |
plan_abyss_eatrest = cascade { | |
{plan_eat_chunk, "eat_chunk"}, | |
{plan_eat_permafood, "eat_permafood"}, | |
{plan_go_to_abyss_exit, "try_go_to_abyss_exit"}, | |
{plan_abyss_hand, "abyss_hand"}, | |
{plan_abyss_rest, "rest"}, | |
{plan_handle_corpses, "handle_corpses"}, | |
{plan_find_corpses, "try_find_corpses"}, | |
{plan_eat_anyway, "eat_anyway"}, | |
} -- hack | |
plan_orbrun_eatrest = cascade { | |
{plan_orbrun_eat_permafood, "orbrun_eat_permafood"}, | |
{plan_orbrun_rest, "orbrun_rest"}, | |
{plan_orbrun_hand, "orbrun_hand"}, | |
} -- hack | |
plan_explore = cascade { | |
{plan_unshaft, "try_unshaft"}, | |
{plan_enter_zig, "enter_zig"}, | |
{plan_continue_travel, "try_continue_travel"}, | |
{plan_enter_portal, "enter_portal"}, | |
{plan_go_to_portal_entrance, "try_go_to_portal_entrance"}, | |
{plan_enter_abyss, "enter_abyss"}, | |
{plan_go_to_abyss_portal, "try_go_to_abyss_portal"}, | |
{plan_go_down_abyss, "try_go_down_abyss"}, | |
{plan_go_to_abyss_downstairs, "try_go_to_abyss_downstairs"}, | |
{plan_autoexplore, "try_autoexplore"}, | |
} -- hack | |
plan_explore2 = cascade { | |
{plan_zig_leave_level, "zig_leave_level"}, | |
{plan_zig_go_to_stairs, "try_zig_go_to_stairs"}, | |
{plan_exit_portal, "exit_portal"}, | |
{plan_go_to_portal_exit, "try_go_to_portal_exit"}, | |
{plan_enter_branch, "try_enter_branch"}, | |
{plan_go_to_zig, "try_go_to_zig"}, | |
{plan_simple_go_down, "try_simple_go_down"}, | |
{plan_new_travel, "try_new_travel"}, | |
} -- hack | |
plan_move = cascade { | |
{plan_stairdance_up, "plan_stairdance_up"}, | |
{plan_emergency, "emergency"}, | |
{plan_retreat_to_upstairs, "retreat_to_upstairs"}, | |
{plan_wait_for_melee, "wait_for_melee"}, | |
{plan_attack, "attack"}, | |
{plan_cure_poison_rotting, "cure_poison_rotting"}, | |
{plan_cure_bad_statdrain, "cure_bad_statdrain"}, | |
{plan_flail_at_invis, "try_flail_at_invis"}, | |
{plan_burn_spellbooks, "try_burn_spellbooks"}, | |
{plan_eatrest, "eatrest"}, | |
{plan_pre_explore, "pre_explore"}, | |
{plan_step_towards_lair, "step_towards_lair"}, | |
{plan_continue_tab, "continue_tab"}, | |
--{plan_abandon_god, "abandon_god"}, | |
{plan_unwield_weapon, "unwield_weapon"}, | |
{plan_join_god, "join_god"}, | |
{plan_find_altar, "try_find_altar"}, | |
{plan_go_to_temple, "try_go_to_temple"}, | |
--{plan_go_to_lair, "try_go_to_lair"}, | |
{plan_explore, "explore"}, | |
{plan_pre_explore2, "pre_explore2"}, | |
{plan_explore2, "explore2"}, | |
{plan_swamp_clear_exclusions, "try_swamp_clear_exclusions"}, | |
{plan_swamp_go_to_rune, "try_swamp_go_to_rune"}, | |
{plan_swamp_clouds_hack, "swamp_clouds_hack"}, | |
{plan_stuck_teleport, "stuck_teleport"}, | |
{plan_stuck, "stuck"}, | |
} -- hack | |
plan_orbrun_move = cascade { | |
{plan_orbrun_emergency, "orbrun_emergency"}, | |
{plan_attack, "attack"}, | |
{plan_cure_poison_rotting, "cure_poison_rotting"}, | |
{plan_cure_bad_statdrain, "cure_bad_statdrain"}, | |
{plan_orbrun_eatrest, "orbrun_eatrest"}, | |
{plan_go_up, "go_up"}, | |
{plan_fly, "fly"}, | |
{plan_find_upstairs, "try_find_upstairs"}, | |
{plan_disturbance_random_step, "disturbance_random_step"}, | |
--{plan_stuck_clear_exclusions, "try_stuck_clear_exclusions"}, | |
{plan_stuck_teleport, "stuck_teleport"}, | |
{plan_autoexplore, "try_autoexplore"}, | |
{plan_gd1, "try_gd1"}, | |
{plan_stuck, "stuck"}, | |
} -- hack | |
plan_abyss_move = cascade { | |
{plan_exit_abyss, "exit_abyss"}, | |
{plan_emergency, "emergency"}, | |
{plan_attack, "attack"}, | |
{plan_cure_poison_rotting, "cure_poison_rotting"}, | |
{plan_cure_bad_statdrain, "cure_bad_statdrain"}, | |
{plan_flail_at_invis, "try_flail_at_invis"}, | |
{plan_abyss_eatrest, "abyss_eatrest"}, | |
{plan_pre_explore, "pre_explore"}, | |
{plan_autoexplore, "try_autoexplore"}, | |
{plan_pre_explore2, "pre_explore2"}, | |
{plan_wait, "wait"}, | |
} -- hack | |
---------------------------------------- | |
-- skill selection | |
function choose_single_skill(sk) | |
you.train_skill(sk, 1) | |
skill_list = {"Fighting","Short Blades","Long Blades","Axes","Maces & Flails", | |
"Polearms","Staves","Unarmed Combat","Bows","Crossbows", | |
"Throwing","Slings","Armour","Dodging","Shields","Spellcasting", | |
"Conjurations","Hexes","Charms","Summonings","Necromancy", | |
"Translocations","Transmutations","Fire Magic","Ice Magic", | |
"Air Magic","Earth Magic","Poison Magic","Invocations", | |
"Evocations","Stealth"} | |
for i,sk2 in ipairs(skill_list) do | |
if sk ~= sk2 then | |
you.train_skill(sk2, 0) | |
end | |
end | |
end | |
function handle_skills() | |
local weapon_skill = wskill() | |
-- need to have at least one skill selected or bad things might happen | |
-- early xom characters might get their weapon removed at any point, so | |
-- always train fighting | |
if you.god() == "Xom" then | |
choose_single_skill("Fighting") | |
you.train_skill(weapon_skill, 1) | |
elseif you.can_train_skill(weapon_skill) then | |
choose_single_skill(weapon_skill) | |
else | |
choose_single_skill("Fighting") | |
end | |
local at_min_delay = (you.base_skill(weapon_skill) >= min_delay_skill()) | |
local ac = armour_ac() | |
if you.base_skill("Shields") < target_shield_skill() | |
and (at_min_delay or you.base_skill(weapon_skill) | |
>= 2*you.base_skill("Shields")) | |
and you.base_skill("Fighting") >= you.base_skill("Shields") then | |
you.train_skill("Shields", 1) | |
elseif intrinsic_heavy_dodgy() and at_min_delay and ac < 12 | |
and you.base_skill("Armour") >= 12 | |
and you.base_skill("Armour") >= you.base_skill("Dodging") | |
and you.base_skill("Fighting") >= you.base_skill("Dodging") then | |
you.train_skill("Dodging", 1) | |
elseif intrinsic_dodgy() and | |
(at_min_delay or you.base_skill(weapon_skill) | |
>= 2*you.base_skill("Dodging")) | |
and you.base_skill("Fighting") >= you.base_skill("Dodging") then | |
you.train_skill("Dodging", 1) | |
elseif (at_min_delay or ac >= 8 and you.base_skill(weapon_skill) | |
>= 3*you.base_skill("Armour")) | |
and you.base_skill("Fighting") >= you.base_skill("Armour") | |
and not intrinsic_dodgy() then | |
you.train_skill("Armour", 1) | |
elseif at_min_delay or you.base_skill(weapon_skill) | |
>= 2*you.base_skill("Fighting") then | |
you.train_skill("Fighting", 1) | |
end | |
if you.race() == "Deep Dwarf" and (you.base_skill("Evocations") < 4 | |
or you.base_mp() < 5) then | |
you.train_skill("Evocations", 1) | |
end | |
if you.race() == "Vine Stalker" and you.base_skill("Evocations") < 12 | |
and (at_min_delay or you.base_skill(weapon_skill) | |
>= 3*you.base_skill("Evocations")) then | |
you.train_skill("Evocations", 1) | |
end | |
if at_min_delay | |
and you.base_skill(weapon_skill) >= 20 | |
and (you.base_skill(weapon_skill) >= you.base_skill("Fighting") | |
or you.base_skill(weapon_skill) >= 26) then | |
you.train_skill(weapon_skill, 0) | |
end | |
-- make sure we don't run out of skills to train | |
if you.base_skill("Fighting") >= 26 then | |
if intrinsic_dodgy() then | |
you.train_skill("Armour", 1) | |
else | |
you.train_skill("Dodging", 1) | |
end | |
end | |
if (you.base_skill("Armour") >= 26 or you.race():find("Draconian") | |
or you.race() == "Felid" | |
or you.race() == "Octopode") | |
and you.base_skill("Dodging") >= 26 then | |
you.train_skill("Evocations", 1) | |
end | |
end | |
function choose_stat_gain() | |
if intrinsic_want_dex() then | |
return "d" | |
else | |
return "s" | |
end | |
end | |
function auto_experience() | |
return true | |
end | |
------------------------------------------- | |
-- a few utility functions | |
function contains_string_in(name,t) | |
for _, value in ipairs(t) do | |
if string.find(name, value) then | |
return true | |
end | |
end | |
return false | |
end | |
function control(c) | |
return string.char(string.byte(c) - string.byte('a') + 1) | |
end | |
function delta_to_vi(dx, dy) | |
local d2v = { | |
[-1] = { [-1] = 'y', [0] = 'h', [1] = 'b'}, | |
[0] = { [-1] = 'k', [1] = 'j'}, | |
[1] = { [-1] = 'u', [0] = 'l', [1] = 'n'}, | |
} -- hack | |
return d2v[dx][dy] | |
end | |
function vi_to_delta(c) | |
local d2v = { | |
[-1] = { [-1] = 'y', [0] = 'h', [1] = 'b'}, | |
[0] = { [-1] = 'k', [1] = 'j'}, | |
[1] = { [-1] = 'u', [0] = 'l', [1] = 'n'}, | |
} -- hack | |
local x,y | |
for x = -1,1 do | |
for y = -1,1 do | |
if supdist(x,y) > 0 and d2v[x][y] == c then | |
return x,y | |
end | |
end | |
end | |
end | |
function sign(a) | |
return a > 0 and 1 or a < 0 and -1 or 0 | |
end | |
function abs(a) | |
return a * sign(a) | |
end | |
function vector_move(dx, dy) | |
local str = '' | |
for i = 1, abs(dx) do | |
str = str .. delta_to_vi(sign(dx), 0) | |
end | |
for i = 1, abs(dy) do | |
str = str .. delta_to_vi(0, sign(dy)) | |
end | |
return str | |
end | |
function supdist(dx, dy) | |
if abs(dx) >= abs(dy) then | |
return abs(dx) | |
else | |
return abs(dy) | |
end | |
end | |
function adjacent(dx, dy) | |
return abs(dx) <= 1 and abs(dy) <= 1 | |
end | |
--------------------------------------------- | |
-- initialization/control/saving | |
function initialize() | |
if not did_first_turn and you.turns() == 0 then | |
did_first_turn = true | |
first_turn() | |
end | |
automatic = true | |
where = you.where() | |
expect_new_location = false | |
if c_persist.branches_entered == nil then | |
c_persist.branches_entered = { "D" } | |
end | |
if c_persist.portals_found == nil then | |
c_persist.portals_found = { } | |
end | |
if c_persist.plan_fail_count == nil then | |
c_persist.plan_fail_count = { } | |
end | |
set_options() | |
initialize_monster_array() | |
end | |
function stop() | |
automatic = false | |
unset_options() | |
end | |
function start() | |
initialize() | |
ready() | |
end | |
function panic(msg) | |
crawl.mpr("<lightred>" .. msg .. "</lightred>") | |
stop() | |
end | |
function startstop() | |
if automatic then | |
stop() | |
else | |
start() | |
end | |
end | |
function hit_closest() | |
startstop() | |
end | |
function set_counter() | |
crawl.formatted_mpr("Set counter to what? ", "prompt") | |
local res = crawl.c_input_line() | |
c_persist.record.counter = tonumber(res) | |
note("counter set to " .. c_persist.record.counter) | |
end | |
function first_turn_persist() | |
if not c_persist.record then | |
c_persist.record = {} | |
end | |
if not c_persist.record.counter then | |
c_persist.record.counter = 1 | |
else | |
c_persist.record.counter = c_persist.record.counter + 1 | |
end | |
note("counter = " .. c_persist.record.counter) | |
--if not c_persist.mlist then | |
-- c_persist.mlist = {} | |
--end | |
--if not c_persist.record.mlist then | |
-- c_persist.record.mlist = {} | |
--end | |
--for _,mname in ipairs(c_persist.mlist) do | |
-- if not c_persist.record.mlist[mname] then | |
-- c_persist.record.mlist[mname] = 1 | |
-- else | |
-- c_persist.record.mlist[mname] = c_persist.record.mlist[mname] + 1 | |
-- end | |
--end | |
for key,_ in pairs(c_persist) do | |
if key ~= "record" then | |
c_persist[key] = nil | |
end | |
end | |
end | |
function first_turn() | |
first_turn_persist() | |
if AUTO_START then | |
initialize() | |
end | |
end | |
function ready() | |
if not did_first_turn and you.turns() == 0 then | |
did_first_turn = true | |
first_turn() | |
end | |
if you.turns() >= dump_count then | |
dump_count = dump_count+100 | |
crawl.dump_char() | |
end | |
if you.turns() >= skill_count then | |
skill_count = skill_count+20 | |
handle_skills() | |
end | |
if you.turns() > old_turn_count then | |
if did_move then | |
move_count = move_count + 1 | |
else | |
move_count = 0 | |
end | |
dd_hw_meter = math.floor(6*dd_hw_meter/7) | |
old_turn_count = you.turns() | |
did_move = false | |
if did_move_towards_monster > 0 then | |
did_move_towards_monster = did_move_towards_monster - 1 | |
end | |
end | |
if you.where() ~= where then | |
clear_ignores() | |
if expect_new_location then | |
if where_shafted_from == you.where() then | |
say("Successfully unshafted to " .. you.where() .. ".") | |
where_shafted_from = nil | |
end | |
elseif automatic and not you.where():find("Abyss") then | |
say("Shafted from " .. where .. " to " .. you.where() .. ".") | |
if not where_shafted_from then | |
where_shafted_from = where | |
end | |
end | |
where = you.where() | |
if cur_branch() and not util.contains(c_persist.branches_entered, cur_branch()) then | |
say("Entered " .. cur_branch() .. ".") | |
table.insert(c_persist.branches_entered, cur_branch()) | |
end | |
if expect_portal and in_portal() then | |
say("Entered " .. where .. ".") | |
end | |
c_persist.portals_found = { } | |
end | |
expect_new_location = false | |
expect_portal = false | |
check_messages() | |
if automatic then | |
crawl.flush_input() | |
crawl.more_autoclear(true) | |
update_monster_array() | |
danger = sense_danger(8) | |
immediate_danger = sense_immediate_danger() | |
sense_sigmund() | |
if have_message then | |
plan_message() | |
elseif you.where():find("Abyss") and not want_to_stay_in_abyss() then | |
plan_abyss_move() | |
elseif you.have_orb() then | |
plan_orbrun_move() | |
else | |
plan_move() | |
end | |
end | |
end | |
function magic(command) | |
crawl.process_keys(command .. string.char(27) .. string.char(27) .. | |
string.char(27)) | |
end | |
-------------------------------- | |
-- a function to test various things conveniently | |
function ttt() | |
--say(you.transform()) | |
--say(view.feature_at(0,0)) | |
--crawl.mpr(slot(1) .. "," .. slot("a")) | |
--say("rot" .. you.rot()) | |
--say(you.hunger_name()) | |
for it in at_feet() do | |
-- if food.bottleable(it) then | |
-- crawl.mpr("bottle") | |
-- end | |
-- if food.edible(it) then | |
-- crawl.mpr("eat") | |
-- end | |
if it.hands then | |
crawl.mpr(it.hands) | |
end | |
-- crawl.mpr(slot(it)) | |
-- if it.ac then | |
-- crawl.mpr(it.ac) | |
-- end | |
-- if it.encumbrance then | |
-- crawl.mpr(it.encumbrance) | |
-- end | |
-- if it.damage then | |
-- crawl.mpr(it.damage) | |
-- end | |
-- if it.accuracy then | |
-- crawl.mpr(it.accuracy) | |
-- end | |
-- if it.delay then | |
-- crawl.mpr(it.delay) | |
-- end | |
end | |
--if you.status("afraid") then | |
-- crawl.mpr("afraid") | |
--end | |
--if you.status("engulfed (cannot breathe)") then | |
-- crawl.mpr("engulfed") | |
--end | |
for x = -8,8 do | |
for y = -8,8 do | |
m = monster.get_monster_at(x, y) | |
if m then | |
crawl.mpr(m:name() .. " | " .. m:desc()) | |
--if is_ranged(m) then | |
-- crawl.mpr("ranged") | |
--end | |
end | |
--cloud = view.cloud_at(x,y) | |
--if cloud then crawl.mpr(x .. "," .. y .. ":" .. cloud) end | |
end | |
end | |
crawl.mpr(tostring(count_permafood())) | |
end | |
function c_trap_is_safe(trap) | |
return (trap ~= "permanent teleport") | |
end | |
local chillax = false | |
local chillax_turn = nil | |
function ch_mon_is_safe(mon) | |
--crawl.mpr(mon:name()) | |
if you.turns() == chillax_turn then | |
return true | |
end | |
end | |
function sc() | |
chillax_turn = you.turns() | |
end | |
function uc() | |
chillax = false | |
end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment