# TEAMCAPTAIN enegeticocto ###THIS IS RC FOR STREAKING!!! ############ GENERAL SETTINGS ############ default_manual_training = true show_more = false use_animations = beam autofight_stop = 70 explore_delay = 1 note_messages += magical essence is drained by the effort ######AUTOPICKUP###### ae := autopickup_exceptions ae += =5 end -- Summon Forest: needs enough open space (at least 4 non-solid tiles in radius 2) local function can_forest() local open=0 for x=-2,2 do for y=-2,2 do if (x~=0 or y~=0) and not is_solid(x,y) then local f=view.feature_at(x,y) if f and f~="X" then open=open+1 end end end end return open>=8 end -- Malign Gateway: needs a floor/water tile 2+ away with 0 solid neighbors local function can_gateway(los) for x=-los,los do for y=-los,los do local d=math.max(math.abs(x),math.abs(y)) if d>=2 and d<=los then local f=view.feature_at(x,y) if f and (f=="." or f=="W") and not monster.get_monster_at(x,y) then local ok=true for dx=-1,1 do for dy=-1,1 do if dx~=0 or dy~=0 then if is_solid(x+dx,y+dy) then ok=false end end end end if ok then return true end end end end end return false end local function count_friends(names,friends) local c=0 for _,f in ipairs(friends) do for _,nm in ipairs(names) do if string.find(f.name,nm) then c=c+1 break end end end return c end -- Find a specific friend by name pattern, return best match local function find_friend(names,friends) for _,f in ipairs(friends) do for _,nm in ipairs(names) do if string.find(f.name,nm) then return f end end end return nil end function bonsai_smart_summon() -- MP setup local mp=_G.you.mp() local ok_race,race=pcall(function() return you.race() end) if ok_race and race=="Djinni" then mp=you.hp()-10 end -- Silence check: can't cast anything local ok_sil,sil=pcall(function() return you.silenced() end) if not ok_sil then ok_sil,sil=pcall(function() return you.status("silenced") end) end if ok_sil and sil then crawl.mpr("Silenced!") return end -- Antimagic debuff: tighten fail threshold local antimagic_debuff=false local ok_am,am=pcall(function() return you.status("antimagic") end) if ok_am and am then antimagic_debuff=true end -- Depth scaling: XL as proxy for game progression -- Low XL (1-10): prefer cheap summons; High XL (18+): prefer powerful local ok_xl,xl=pcall(function() return you.xl() end) local depth_tier=2 if ok_xl and xl then if xl<=10 then depth_tier=1 elseif xl>=18 then depth_tier=3 end end -- Reserve MP for escape spells local escape_spells={"Blink","Passage of Golubria","Dispersal","Disjunction"} local reserve_mp=0 local reserve_spell=nil for _,esc in ipairs(escape_spells) do if spells.memorised(esc) then local c=spells.mana_cost(esc) if c and c>reserve_mp then reserve_mp=c;reserve_spell=esc end end end -- Scan LOS for enemies and friendlies local los=you.los() local t={} local friends={} for x=-los,los do for y=-los,los do local m=monster.get_monster_at(x,y) if m and not m:is_firewood() then if m:attitude()==0 then t[#t+1]={m=m,x=x,y=y} elseif m:attitude()>0 then local ok_dl,dl=pcall(function() return m:damage_level() end) local hr=1.0 if ok_dl and dl then hr=(6-dl)/6 end friends[#friends+1]={name=m:name(),x=x,y=y,hp_ratio=hr} end end end end if #t==0 then crawl.mpr("No enemies") return end -- Situation assessment with threat weighting local hp,maxhp=you.hp() local hp_ratio=hp/maxhp local adj_hostiles=0 local total_hostiles=#t local total_threat=0 local max_single_threat=0 local has_caster=false local has_ranged_enemy=false local min_enemy_dist=99 for _,e in ipairs(t) do local d=cdist(e.x,e.y) e.dist=d if d<=1 then adj_hostiles=adj_hostiles+1 end if dmax_single_threat then max_single_threat=ehp end local ok_sp,sp=pcall(function() return e.m:spells() end) if ok_sp and sp and #sp>0 then has_caster=true if d>=4 then has_ranged_enemy=true end end -- Count neighbors for positioning awareness local nb=0 for _,e2 in ipairs(t) do if e2~=e and math.abs(e.x-e2.x)<=1 and math.abs(e.y-e2.y)<=1 then nb=nb+1 end end e.neighbors=nb end -- Escape urgency: 0.0 (safe) to 2.0 (desperate) local escape_urgency=0 if hp_ratio<0.3 then escape_urgency=2.0 elseif hp_ratio<0.5 then escape_urgency=1.0+(0.5-hp_ratio)*4 elseif adj_hostiles>0 then escape_urgency=0.5+adj_hostiles*0.3 end -- Swarm factor: threat-weighted (120 HP ~ 3 medium enemies) local swarm_factor=math.min(2.0,total_threat/120) -- Tank need: scales with strongest adjacent enemy local tank_need=0 if adj_hostiles>0 and hp_ratio>=0.5 then tank_need=1.0+math.min(max_single_threat/40,2.0) end -- Corridor detection: boost tanks, reduce multi local in_corridor=detect_corridor() if in_corridor then tank_need=tank_need*1.5 swarm_factor=swarm_factor*0.5 end -- Retreat mode: low HP, no adjacent enemies = need blockers to disengage local retreating=(hp_ratio<0.3 and adj_hostiles==0) -- Focus fire: detect wounded enemies (current HP < max HP) local has_wounded=false for _,e in ipairs(t) do local ok_dl,dl=pcall(function() return e.m:damage_level() end) if ok_dl and dl and dl>=2 then has_wounded=true break end end -- Turn efficiency: enemies far = time for expensive/delayed summons local have_time=(min_enemy_dist>=4) -- Manual mode trigger: only low HP local manual_mode=(hp_ratio<0.5) -- Combo awareness: categorize existing summons on field local has_tank_friend=false local has_ranged_friend=false local has_blocker=false local tank_names={"phalanx beetle","animated armour","armour echo","cactus giant", "platinum paragon","hydra","ice beast","inugami","crocodile","martyred shade"} local ranged_names={"lightning spire","spellforged servitor","blazeheart golem", "walking alembic","mana viper","cerulean imp"} for _,f in ipairs(friends) do for _,nm in ipairs(tank_names) do if string.find(f.name,nm) then has_tank_friend=true break end end for _,nm in ipairs(ranged_names) do if string.find(f.name,nm) then has_ranged_friend=true break end end for _,e in ipairs(t) do if math.abs(f.x-e.x)<=1 and math.abs(f.y-e.y)<=1 then has_blocker=true end end end -- Score each summon spell local candidates={} local almost={} for _,sp in ipairs(summons) do if spells.memorised(sp.n) then local cost=spells.mana_cost(sp.n) local fail=spells.fail(sp.n) local ok_sev,sev=pcall(function() return spells.fail_severity(sp.n) end) local fail_cap=antimagic_debuff and 10 or 20 if fail0 and current>=sp.cap and not sp.recast if not at_cap then -- Special precondition checks local skip=false -- Awaken Armour needs body armour if sp.needs_armour then local ok_arm,arm=pcall(function() return items.equipped_at("body armour") end) if not ok_arm or not arm then skip=true end end -- Dragon Call: skip if already active if sp.channeled and sp.n=="Dragon's Call" then local ok_st,st=pcall(function() return you.status("Dragon's Call") end) if ok_st and st then skip=true end end -- Call Canine Familiar: skip if on death cooldown if sp.n=="Call Canine Familiar" then local ok_cd,cd=pcall(function() return you.status("unable to call your familiar") end) if ok_cd and cd then skip=true end end -- Surprising Crocodile needs adjacent enemy to drag if sp.n=="Eringya's Surprising Crocodile" and adj_hostiles==0 then skip=true end -- Summon Forest: needs open space + not already active if sp.needs_space then local ok_sf,sf=pcall(function() return you.status("forested") end) if (ok_sf and sf) or not can_forest() then skip=true end end -- Malign Gateway: needs open floor tile 2+ away if sp.needs_gateway and not can_gateway(los) then skip=true end if not skip then local pow=spells.power_perc(sp.n) or 0 local base=sp.power(pow) -- Situational bonuses local escape_bonus=sp.escape*escape_urgency*3 local tank_bonus=sp.tank*tank_need*2 local multi_bonus=sp.multi*swarm_factor*2 -- Antimagic bonus for Mana Viper vs casters local antimagic=0 if sp.n=="Summon Mana Viper" and has_caster then antimagic=15 end -- Ball Lightning: dangerous if adjacent enemies local danger_mult=1.0 -- Seismosaurus Egg: delayed, less useful in combat if sp.delayed then danger_mult=danger_mult*0.5 end local mp_bonus=0 -- Canine Familiar recast: only worth it if familiar is badly hurt if sp.recast and sp.n=="Call Canine Familiar" and current>=sp.cap then local f=find_friend(sp.fn,friends) if f and f.hp_ratio<0.5 then danger_mult=danger_mult*1.2 else danger_mult=0 end end -- Platinum Paragon recast: boost if enemies are clustered if sp.recast and sp.n=="Platinum Paragon" and current>=sp.cap then if total_hostiles>=3 then danger_mult=danger_mult*1.5 else danger_mult=danger_mult*0.8 end end -- Crocodile: massive boost when enemies adjacent (escape + repositioning) local adj_bonus=0 if sp.adj_boost and adj_hostiles>0 then adj_bonus=40+adj_hostiles*15 end -- Combo synergy multipliers local combo_mult=1.0 if has_tank_friend and sp.role=="ranged" then combo_mult=combo_mult*1.4 end if has_ranged_friend and sp.role=="tank" then combo_mult=combo_mult*1.4 end if has_blocker and sp.role=="multi" then combo_mult=combo_mult*1.3 end -- Retreat mode: prefer blockers (high escape+tank), penalize pure DPS if retreating then if sp.escape>=5 or sp.tank>=7 then combo_mult=combo_mult*1.6 end if sp.role=="ranged" and sp.tank<=3 then combo_mult=combo_mult*0.5 end end -- Focus fire: boost fast ranged DPS to finish wounded enemies if has_wounded and sp.role=="ranged" then combo_mult=combo_mult*1.3 end -- Ranged enemy priority: boost summons that can reach distant casters if has_ranged_enemy then if sp.n=="Summon Mana Viper" then combo_mult=combo_mult*1.5 elseif sp.role=="ranged" then combo_mult=combo_mult*1.2 end end -- Turn efficiency: far enemies = time for delayed/expensive; close = instant value if have_time then if sp.delayed then danger_mult=danger_mult*1.5 end if sp.channeled then combo_mult=combo_mult*1.3 end if sp.role=="multi" then combo_mult=combo_mult*1.2 end else if sp.delayed then danger_mult=danger_mult*0.3 end end -- Depth scaling: early game prefers cheap, late game prefers powerful if depth_tier==1 and cost>=7 then combo_mult=combo_mult*0.6 elseif depth_tier==3 and cost<=3 then combo_mult=combo_mult*0.5 end -- Clamp combo_mult to prevent extreme score inflation/deflation combo_mult=math.max(0.2,math.min(combo_mult,3.0)) local raw_score=base+escape_bonus+tank_bonus+multi_bonus+antimagic+mp_bonus+adj_bonus local val=raw_score*danger_mult*combo_mult/math.sqrt(cost) -- Find best target for targeted spells local tx,ty=0,0 if sp.target_enemy then local best_thr=0 local range=spells.range(sp.n) or los for _,e in ipairs(t) do local d=cdist(e.x,e.y) if d<=range then -- Crocodile: prefer adjacent; others: prefer strongest + clustered local thr=1 if sp.n=="Eringya's Surprising Crocodile" then if d<=1 then thr=10 else thr=0 end else local ok_hp,mhp=pcall(function() return e.m:max_hp() end) if ok_hp and mhp then local hp_str=tostring(mhp) thr=tonumber(hp_str:match("%d+")) or 10 end -- Positioning: prefer targets surrounded by other enemies thr=thr*(1+0.2*(e.neighbors or 0)) end if thr>best_thr then best_thr=thr;tx=e.x;ty=e.y end end end if best_thr==0 then skip=true end end if not skip then if cost<=mp then candidates[#candidates+1]={n=sp.n,v=val,c=cost,x=tx,y=ty, short=sp.short,escape=sp.escape,tank=sp.tank,multi=sp.multi, target_enemy=sp.target_enemy} elseif cost<=mp+1 then almost[#almost+1]={n=sp.n,v=val,c=cost,short=sp.short} end end end -- not skip (preconditions) end -- not at_cap end -- fail check end -- memorised end -- spell loop if #candidates==0 and #almost==0 then crawl.mpr("No summon") return end -- Sort by value table.sort(candidates,function(a,b) return a.v>b.v end) -- Manual mode: show rated list, let player choose if manual_mode and #candidates>0 then local msg="" local shown=math.min(5,#candidates) for i=1,shown do local c=candidates[i] local tag="dmg" if c.escape>6 then tag="ESC" elseif c.tank>6 then tag="TNK" elseif c.multi>6 then tag="AOE" end local ok_let,letter=pcall(function() return spells.letter(c.n) end) local let=ok_let and letter or "?" if i>1 then msg=msg.." " end msg=msg..let..":"..c.short.."("..tag..")" end crawl.mpr(msg) return end -- Wait-for-1-MP check if #candidates>0 and #almost>0 then table.sort(almost,function(a,ab) return a.v>ab.v end) if almost[1].v>candidates[1].v*1.5 then crawl.mpr("Wait 1MP for "..almost[1].short) return end end -- Try candidates in order; skip to next on cast failure (check MP change) local cast_ok=false for _,b in ipairs(candidates) do if reserve_mp>0 and (mp-b.c) ## macros += M 9 G\{13} bindkey = [f6] CMD_DISPLAY_SPELLS dos_use_background_intensity = true # Add the following to your options file to automatically pick up # armour for non-body armour slots (gloves, boots, etc.), if you don't # already have an item equipped there. { add_autopickup_func(function(it, name) if it.is_useless then return end if it.class(true) == "armour" then local good_slots = {cloak="Cloak", helmet="Helmet", gloves="Gloves", boots="Boots"} st, _ = it.subtype() if good_slots[st] ~= nil and items.equipped_at(good_slots[st]) == nil then return true end end end) }