require 'libmh' class Mounty def self.dice(d, nd=1) Array.new(nd) { 1+rand(d) }.inject { |a, b| a+b } end # find the dest so that we travel diag from from and arrive at same level # eg from 000: (0 -> -1 has +1 xy bonus) # [8, 8, -4] => [5, 5, -4] # [8, 8, -16] => [8, 8, -16] # [2, 8, -4] => [2, 5, -4] def dest_diag(dst, from=pos) dxy = [(dst[0]-from[0]).abs, (dst[1]-from[1]).abs].max dxy -= 1 if from[2] == 0 dz = (dst[2] - from[2]).abs d = dxy-dz if d > 0 dst = dst.dup if (dst[0] - from[0]).abs > d dst[0] -= (dst[0]>from[0]) ? d : -d end if (dst[1] - from[1]).abs > d dst[1] -= (dst[1]>from[1]) ? d : -d end end dst end # returns the array of pos in the range from dst (same level) def dest_range(dst, hzmax, hzmin = 0) return [dst] if hzmax == 0 ret = [] (hzmin..hzmax).each { |d1| (-hzmax..hzmax).each { |d2| ret << dst.dup ret.last[0] -= d1 ret.last[1] += d2 ret << dst.dup ret.last[0] += d1 ret.last[1] += d2 ret << dst.dup ret.last[0] += d2 ret.last[1] -= d1 ret << dst.dup ret.last[0] += d2 ret.last[1] += d1 } } ret.uniq end def dist_to_direct(dst, from = pos) dhz = [ (from[0] - dst[0]).abs, (from[1] - dst[1]).abs ].max dn = (from[2] - dst[2]).abs d = dn + [dhz, dn].max d += 1 if from[2] == 0 and dst[2] < 0 d += 1 if @case_occupee and dst != from and from == pos d end def dist_to_surf(dst, from = pos) dhz = [ (from[0] - dst[0]).abs, (from[1] - dst[1]).abs ].max cout = 0 cout += 1 if @case_occupee and dst != from and from == pos cout += from[2].abs * 2 ; dhz -= from[2].abs # remonte if dst[2] < 0 cout += 1 + dst[2].abs * 2 ; dhz -= dst[2].abs + 1 # redescend end dhz = 0 if dhz < 0 cout + (dhz + 1)/2 # surface end # dst = [x, y, n], hz = horizontal distance on the same level def dist_to(dst, from=pos, hzmax = 0, hzmin = 0) dest_range(dst, hzmax, hzmin).map { |dst| [dist_to_surf(dst, from), dist_to_direct(dst, from)].min }.min end def bot_getdest # TODO mode => nearest_aub ? d = File.read('dest').delete('[]').split(/[ ,]+/).map { |i| i.to_i }[0, 3] d.length != 3 ? pos : d rescue pos end def bot_move_to_dest(dst=bot_getdest, hzmax=0, hzmin=0) case dst when Integer; return if not dst = vue.all.find { |a| a.id == dst } || vue.all.find { |a| a.id.to_s =~ /#{dst}/ } ; dst = dst.pos when String; return if not dst = vue.all.find { |a| a.nom =~ /#{dst}/i } ; dst = dst.pos end if self['portation'] and dist_to(dst) > 16 and self[:pa] >= 6 and self[:n] != 0 and vue(0).lieux.empty? tp(*dst) elsif self['portation'] and dist_to(dst) > 16 and self[:pa] >= 3 and self[:n] == 0 and vue(2).lieux.empty? move_to_dest [self[:x] + rand(3)-1, self[:y] + rand(3)-1, -1] elsif l = vue(0).lieux.first and l.nom =~ /portail de t.*portation/i and pdst = portail(:dest) and dist_to_direct(dst) > dist_to_direct(dst, pdst) + 8 portail :prendre if self[:pa] >= 4 elsif dist_to(dst) > 12 and l = vue.lieux.find { |l| l.dist > 0 and l.nom =~ /portail de t.*portation/i and dist_to(l.pos) + dist_to_direct(dst, l.pos) <= dist_to(dst) } move_to_dest l.pos elsif self['portation'] and dist_to(dst) > 16 and dist_to_surf(dst) < dist_to_direct(dst) # avoid 0 -> -1 -> 0 move_to_dest dest_diag(dst) else move_to_dest dst, hzmax, hzmin end end def nearest_aub if File.exist? 'aub_list' aub_list = File.read 'aub_list' else aub_list = < EOL end aub_list = parsehtml(aub_list).map { |e| %w[x y z].map { |a| e[a].to_i } } aub_list.sort_by { |a| dist_to a }.first end def chargedist Mounty.portee((self[:pv].to_i + 9) / 10 + self[:reg].to_i) - self[:fatigue].to_i/5 end def projodist Mounty.portee(self[:vue_tot]) end def teamlist(with_self = true) @teamlist ||= if File.exist? 'teamlist' File.read('teamlist').split.map { |i| i.to_i } else [self[:mat]] end with_self ? @teamlist : (@teamlist - [self[:mat]]) end MonstreCache = 'monstre_niveau' MonstreCacheServer = HttpClient.new 'trolls.ratibus.net' # 'trolls.game-host.org' def load_monstre_cache return if defined? @monstre_cache if File.exist? MonstreCache @monstre_cache = {} IO.foreach(MonstreCache) { |l| s = l.chomp.split(';') @monstre_cache[s[0]] = s[1].to_i if s[1].to_i != 0 } else @monstre_cache = nil end end def save_monstre_cache if @monstre_cache mc = @monstre_cache.map { |m, n| "#{m};#{n}\n" }.sort.join if File.read(MonstreCache) != mc File.open(MonstreCache + '.tmp', 'w') { |fd| fd.puts mc } File.rename(MonstreCache + '.tmp', MonstreCache) system('ruby update.rb put ' + MonstreCache) if File.exist? 'update.rb' end end end def monstre_niveau(nom) load_monstre_cache return if not @monstre_cache return @monstre_cache[nom] if @monstre_cache[nom] return if not File.writable?(MonstreCache) or File.symlink?(MonstreCache) # http://trolls.game-host.org/mountyhall/cdm.php?monstre=Ma%EEtre+Squelette+%5BNaissant%5D req = '/mountyhall/cdm.php?monstre=' req += HttpServer.urlenc nom begin p = MonstreCacheServer.get(req, 1).parse go = false p.each { |e| case e.type when 'String' if go niv = e['content'][/ (\d+)$/, 1].to_i if niv != 0 puts "#{nom} => nv #{niv}" @monstre_cache[nom] = niv end break end when 'h1'; go = true end } save_monstre_cache rescue puts "get monstre_niveau: error #{$!}, #{$!.message}" rescue nil end @monstre_cache[nom] end def vue_filt(*a) v = vue(*a) v.tresors.delete_if { |e| e.nom =~ /bidouille/i or dist_to(e.pos) > 11 } v.monstres.delete_if { |e| e.nom =~ /Gowap Appr|Familier|niym/i } # liste des positions des trolls de la team teamtrollspos = v.trolls.find_all { |e| teamlist.include? e.id and e.dist != 0 }.map { |e| e.pos } + [self.pos] trouspos = v.lieux.find_all { |e| e.nom =~ /^Trou de / }.map { |e| e.pos } # liste des positions des trolls sans trolls de la team sur la case trollspos = v.trolls.map { |e| e.pos }.uniq - teamtrollspos v.monstres.delete_if { |e| trollspos.include? e.pos or trouspos.include? e.pos } v.tresors.delete_if { |e| trouspos.include? e.pos or (e.dist > 0 and e.nom != 'Parchemin' and (trollspos.include? e.pos or teamtrollspos.include? e.pos)) } v.lieux.delete_if { |e| e.dist > 2 and e.nom =~ /Trou de M.t.orite/i } # reorder by dist [:monstres, :trolls, :lieux, :tresors, :champignons].each { |cat| v.send(cat).replace v.send(cat).sort_by { |e| [dist_to(e.pos), e.n, e.x, e.y, e.id] } } v end attr_accessor :pos_occ_cb def move_to_dest(dst, hzmax = 0, hzmin = 0) case dst when Integer; return if not dst = vue.all.find { |a| a.id == dst } || vue.all.find { |a| a.id.to_s =~ /#{dst}/ } ; dst = dst.pos when String; return if not dst = vue.all.find { |a| a.nom =~ /#{dst}/i } ; dst = dst.pos end dst = dst.pos if dst.respond_to? :pos profil dst = dest_range(dst, hzmax, hzmin).sort_by { |tdst| [[dist_to_surf(tdst), dist_to_direct(tdst)].min, dist_to_direct(dst, tdst)] }.first dx, dy, dn = dst.zip(pos).map { |a, b| a-b } if dist_to_surf(dst) < dist_to_direct(dst) and self[:n] != 0 dn = -self[:n] elsif dn < 0 and [dx.abs, dy.abs].max-((self[:n] == 0) ? 1 : 0) > dn.abs # descend en fin de parcours, pour optimiser les chutes et la surface dn = 0 elsif dn > 0 and dn < 4 and [dx.abs, dy.abs].max - dn > 3 # evite de remonter a tout prix (la cible risque d'etre redescendue le temps qu'on se rapproche) dn = 0 end return unless opt = (pre_move(true) if self[:clair]) || pre_move(false) mvrel = opt.zip([dx, dy, dn]).map { |can, want| can.sort_by { |c| (c - want).abs }.first } latt = [dx, dy, dn].map { |c| c.abs }.max return if mvrel[2] == 0 and latt == dn.abs # evite de gacher des PAs si le changement de niveau est necessaire if latt > 1 and (dx.abs < latt or dy.abs < latt) # contournement trolls/monstres opt[0].delete_if { |can| (dx - can).abs >= latt } ; opt[0] << mvrel[0] if opt[0].empty? # should never happen opt[1].delete_if { |can| (dy - can).abs >= latt } ; opt[1] << mvrel[1] if opt[1].empty? mvrel_opt = [] opt[0].each { |canx| opt[1].each { |cany| mvrel_opt << [canx, cany, mvrel[2]] } } st = @http_client.status_save v = vue(2) occ = v.anim.map { |e| e.pos }.uniq occ |= @pos_occ_cb[] if pos_occ_cb trous = v.lieux.find_all { |e| e.nom =~ /^Trou de / }.map { |e| e.pos } @http_client.status_restore st mvrel_opt = mvrel_opt.sort_by { |omvrel| (dx - omvrel[0]).abs + (dy - omvrel[1]).abs } mvrel = mvrel_opt.find { |omvrel| not (occ+trous).include? pos.zip(omvrel).map { |c, dc| c+dc } } || mvrel_opt.find { |omvrel| not trous.include? pos.zip(omvrel).map { |c, dc| c+dc } } || mvrel p :dbg_mv_trous, trous, :mvrel, mvrel, :opts, mvrel_opt if not trous.empty? end do_move_rel(*mvrel) end def amelioration(what = nil) puts "amelioration #{what}" p = use_action 6 s = p.parse('Script').first return false if s['content'] !~ /parent.Contenu.location.href='(.*)';/i @http_client.get_url_allowed << @http_client.abs_path($1) @http_client.fetch_next_now p = get $1 if not what p.parse.each { |e| case e.type when 'form', '/form', 'input' puts e when 'String' puts e['content'] end } nil else curform = nil curid = nil curval = nil p.parse.each { |e| case e.type when 'form' curform = e['action'] # select the form to post (1st for caracs or 2nd for comps) # XXX the page has
...... when 'input' curid, curval = e['name'], e['value'] if curval =~ /#{what}/i pd = @http_client.post_allowed.find { |pd| pd.url.include? curform } next if not pd post pd.url, pd.vars.merge(curid => curval) show get_confirm break end when 'String' if e['content'] =~ /#{what}/i and curid pd = @http_client.post_allowed.find { |pd| pd.url.include? curform } return false if not pd post pd.url, pd.vars.merge(curid => curval) show get_confirm break end end } nil end end alias amelio amelioration def bot_ramasse(t) case t when Integer; t = vue.tresors.find { |e| e.id == t } || vue.tresors.find { |e| e.id.to_s.include? t.to_s } when '.' when String; t = vue.tresors.find { |e| e.nom =~ /#{t}/i } end return if not t if self[:kin] return if not t = vue_filt(1).tresors.first if t == '.' malus = -2 malus = [2, (self[:vue_tot]+1)/2].min if t.nom =~ /Parchemin|Gigots|Composant/i pt = [self[:vue_tot] / 2 + malus, 0].max pt = 3 if pt > 3 if t.n != self[:n] or t.dist > pt ; move_to_dest t.pos, pt ; false elsif t.dist > 1 ; telek(t.id) ; false else r = telek(t.id) ; r && r.reussi end else return if not t = vue_filt(0).tresors.first if t == '.' if t.dist > 0 ; move_to_dest t.pos ; false else ; ramasser t.id ; true end end end def cout_amelio(what) cst = 16 case what.to_s.downcase when /vue/ cnt = self[:vue] - 2 res = true if self[:race] == 'Tomawak' when /att/ cnt = self[:att] - 2 res = true if self[:race] == 'Skrim' when /esq/ cnt = self[:esq] - 2 when /deg/ cnt = self[:deg] - 2 res = true if self[:race] == 'Kastar' when /pv/ cnt = self[:pvmax] / 10 - 2 res = true if self[:race] == 'Durakuir' when /reg/ cnt = self[:reg] cst = 30 when /dla/ cur = self[:duree_tour] cur = cur[0] * 60 + cur[1] base = 12*60 inc = 30 cnt = 1 while base > cur cnt += 1 base -= inc inc -= 3 inc = 2.5 if inc < 2 end cst = 18 end if res cnt -= 1 cst = 12 end cnt * cst end def each_pa profil while @status[:pa] > 0 laststate = [@status[:pa], pos, @status[:tried_comp], @status[:tried_sort]] yield profil break if laststate == [@status[:pa], pos, @status[:tried_comp], @status[:tried_sort]] end nil end def until_px profil curpx = @status[:px] each_pa { yield ; profil ; break true if @status[:px] != curpx } end def until_success each_pa { r = yield ; profil ; break true if r and r.reussi } end def until_sr each_pa { r = yield ; profil ; break true if r and r.reussi and r.jetsr > r.sr } end def enchanteur(action=nil, target=nil) case action when :valider, :valide lieu get '/mountyhall/MH_Lieux/Lieu_Enchanteur.php' show get_confirm show show nil, false, false when :accepter, :refuser, :accepte, :refuse pd = @http_client.post_allowed.first case action when :accepter, :accepte pd.vars.delete_if { |k, v| k =~ /refus/i } when :refuser, :refuse pd.vars.delete_if { |k, v| k =~ /accept/i } end post pd.url, pd.vars show nil, false, false get_confirm else lieu get '/mountyhall/MH_Lieux/Lieu_Enchanteur.php' if not target show nil, false show nil, true, false else pd = @http_client.post_allowed.first post pd.url, pd.vars.merge('ai_IDTE' => target.to_s) puts txt = get_text get_confirm message_send self[:id], "Enchanteur #{pos * ','}", "Enchantement de #{target}:\n" + txt end end end def portail(action = nil) lieu(false) case action when :dest if get_text =~ /portail .* en X = (-?\d+) .* Y = (-?\d+) .* N = (-?\d+)/i show [$1.to_i, $2.to_i, $3.to_i] end when :prendre get "/mountyhall/MH_Lieux/Lieu_Teleport.php" get_confirm show get_confirm profil else show nil, false end end def maudire(troll, objid) troll, objid = objid, troll if troll.to_i > 400_000 and troll.to_i > objid.to_i puts "maudit #{troll} avec #{objid}" lieu get '/mountyhall/MH_Lieux/Lieu_Envouteur.php' return if not pd = http_client.post_allowed.first post pd.url, pd.vars.merge('ai_IDPJ' => troll.to_s, 'ai_IDTE' => objid.to_s) show get_confirm end def desenvouter lieu get 'Lieu_Desenvouteur.php' post 'Lieu_Desenvouteur2.php', 'as_Desenvouter' => "Demander le d\351senvo\373tement (5 PA)" show get_confirm end alias desenvoute desenvouter # auberge(:vue | :contact, pnj | :discut, pnj | :vomir) def auberge(action, target=nil, subj=nil) case action when :vue lieu p = get '/mountyhall/MH_Lieux/Lieu_Auberge.php' show_table_content p, target when :vomir get '/mountyhall/MH_Play/Actions/Play_a_Vomir.php' when :contact get '/mountyhall/MH_Play/Actions/Play_a_PriseContactPNJ.php' pd = @http_client.post_allowed.first if not target show return pd.vars['ai_IdTarget'][1..-1] end post pd.url, pd.vars.merge('ai_IdTarget' => target.to_s) show get_confirm profil auberge nil, true when :discut get '/mountyhall/MH_Play/Actions/Play_a_DiscuterPNJ.php' pd = @http_client.post_allowed.first if not target return pd.vars['ai_IdTarget'][1..-1] end if not subj return pd.vars['ai_IdSujet'][1..-1] end post pd.url, pd.vars.merge('ai_IdTarget' => target.to_s, 'ai_IdSujet' => subj.to_s) t = get_text puts t get_confirm profil auberge nil, true t end end def forger(act, *a) lieu get '/mountyhall/MH_Lieux/Lieu_Forgeron.php' case act when :forge, :forger # :forge, 'bap', 1234 (id objet a fondre), 4567 # :forge, :recuperer get 'Lieu_ForgeronForger.php' pd = @http_client.post_allowed.first tg = a.shift case tg when 'bap'; tg = '262' when Integer; tg = tg.to_s when :recuperer if not pd show return end post pd.url, pd.vars show if pd.url =~ /supplement/i foo = a.shift pd = @http_client.post_allowed.first pd.vars.delete_if { |k, v| k =~ /as_Select/i and k !~ /#{a}/ } pd.vars['as_Action'] = "Donner le Mithril (1 PA)" post pd.url, pd.vars show end get_confirm return else show nil, true, false return end post pd.url, pd.vars.merge('ai_IdTarget' => tg) pd = @http_client.post_allowed.first h = { 'ai_IdTarget' => pd.vars['ai_IdTarget'], 'as_Action' => pd.vars['as_Action'] } a.flatten.each { |meltid| h["as_Select_#{meltid}"] = '1' } post pd.url, h show get_confirm when :repare, :reparer get 'Lieu_ForgeronReparer.php' show nil, true, false when :estime, :estimer get 'Lieu_ForgeronEstimer.php' show nil, true, false when :mm get 'Lieu_ForgeronMM.php' show nil, true, false when :rm get 'Lieu_ForgeronRM.php' show nil, true, false end end alias forge forger # each_pa { move_to_dest nearest_aub } # each_pa { bot_auberge } def bot_auberge if dist_to(nearest_aub) > 0 bot_move_to_dest nearest_aub profil return end v = auberge(:vue) if v.empty? move_to_dest [pos[0]+1, pos[1], pos[2]] return end known, unknown = v.find_all{ |e| e[2] == 'PNJ' }.partition { |e| e[1] != 'INCONNU' } known.delete_if { |e| e[4] =~ /agressi|hostil|insultant/ } goodknown = known.find_all { |e| e[4] =~ /fraternel|cordial|amical/ } if defined? @auberge_bl and @auberge_bl goodknown.delete_if { |e| @auberge_bl.include? e[0] } known.delete_if { |e| @auberge_bl.include? e[0] } elsif File.exist? 'auberge_bl' if File.stat('auberge_bl').mtime < Time.now - 15*24*3600 File.unlink('auberge_bl') else @auberge_bl = File.read('auberge_bl').split end end if unknown.length > 0 and (self[:pa] == 1 or known.empty? or rand(4) == 1 or (goodknown.empty? and rand(2) == 1)) auberge :contact, unknown[rand(unknown.length)].first elsif self[:pa] >= 2 and known.length > 0 known = goodknown if goodknown.length > 0 and rand(4) != 1 tg = known[rand(known.length)] lsj = auberge :discut, tg.first sj = lsj[rand(lsj.length)] puts "discution avec #{tg.inspect}, sj #{sj}" res = auberge :discut, tg.first, sj case res when /aurai.* bien aim.* donner .* plus rien sur moi/ (@auberge_bl ||= []) << tg.first File.open('auberge_bl', 'w') { |fd| fd.puts @auberge_bl.join(' ') } system 'ruby update.rb yell auberge_bl' if File.exist? 'update.rb' end end end # topic in [:accepter, :equipe, :etapes, :recompenses] # action in [:valider, :recruter, :passer_flambeau, :virer] def mission(topic=nil, action=nil, target=nil) menu_get '/mountyhall/MH_Missions/Mission_Description.php' case topic when :accepter, :accepte pd = @http_client.post_allowed.first action ||= pd.vars['ai_Mission'].first if pd and pd.vars['ai_Mission'] and pd.vars['ai_Mission'].length == 1 if action post pd.url, pd.vars.merge('ai_Mission' => action.to_s) show get_confirm else pd.vars['ai_Mission'] if pd end when :equipe get '/mountyhall/MH_Missions/Mission_Equipe.php' rescue (show; return) case action when :accepter, :accepte pd = @http_client.post_allowed.first post pd.url, pd.vars.merge('as_Action' => 'Rejoindre la Mission') show mission :equipe when :refuser, :refuse pd = @http_client.post_allowed.first post pd.url, pd.vars.merge('as_Action' => 'Refuser la Mission') show mission when :recruter, :recrute get '/mountyhall/MH_Missions/Mission_Recrute.php' pd = @http_client.post_allowed.first if not target puts "Trolls recrutable: ", pd.vars['ai_SelectedNewEquipier'] return end pd.vars['ai_SelectedNewEquipier'] = pd.vars['ai_SelectedNewEquipier'].grep(/#{target}/).first || raise('no such choice to recruit') post pd.url, pd.vars show get_confirm when :passer_flambeau, :passe_flambeau get '/mountyhall/MH_Missions/Mission_NewMeneur.php' pd = @http_client.post_allowed.first pd.vars['ai_SelectedNewMeneur'] = pd.vars['ai_SelectedNewMeneur'].grep(/#{target}/).first || raise('no such choice to newmeneur') post pd.url, pd.vars when :virer, :vire pd = @http_client.post_allowed.first show nil, true, false p pd return pd.vars.delete_if { |k, v| k =~ /ai_Rang/ and k !~ /#{target}/ } puts "mission: exclusion de #{pd.vars.keys.grep(/ai_Rang/).inspect}" sleep 10 post pd.url, pd.vars get_confirm show else show end when :etapes, :etape get '/mountyhall/MH_Missions/Mission_Etape.php' rescue (show; return) case action when :valider, :valide pd = @http_client.post_allowed.first post pd.url, pd.vars show get_confirm else show end when :recompenses, :recompense get '/mountyhall/MH_Missions/Mission_Recompense.php' rescue (show; return) case action when :accepter, :accepte if target.kind_of? Hash pd = @http_client.post_allowed.first yield pd if block_given? post pd.url, pd.vars.merge(target) show get_confirm else show nil, true, false end when :cheat # target = [[regle1, tresorrecompenseid1, choix1], [regle2, ...]] (ints) pd = @http_client.post_allowed.first pd.mandatory.clear tg = {} tg['as_Action'] = pd.vars['as_Action'] tg['ai_IdMission'] = pd.vars['ai_IdMission'] tg['ai_NbRecompense'] = target.length.to_s puts "mission cheat #{target.inspect}" target.each_with_index { |(r, i, c), ix| ix += 1 tg["ai_Regle#{ix}"] = r.to_s tg["ai_TresorRecompenseID#{ix}"] = i.to_s tg["as_Choix#{ix}"] = c.to_s } pd.vars = tg post pd.url, pd.vars show get_confirm else show nil, false end else show nil, false, false end end def readall_msg(n = -1) messages[0...n].reverse_each { |m| messages m } end def readall_msgbot(n = -1) messages_bot[0...n].reverse_each { |m| messages_bot m } end def self.bot_mainloop(logfile) fork and exit date = Time.now loop do delay = Mounty.dice(20*60*3, 4) # 0-4h File.open(logfile, 'a') { |fd| fd.puts "mainloop sleep #{delay/3600}h#{(delay%3600)/60} => #{(Time.now + delay).strftime('%d/%m %H:%M')}" } rescue nil system 'ruby update.rb put log_mh' if File.exist? 'update.rb' sleep delay if Time.now - date < 20*60 date = Time.now system 'ruby update.rb update' if File.exist? 'update.rb' system "ruby #$0 #{ARGV * ' '} >> #{logfile} 2>&1" end end def run_script(name) if File.exist? name script, postpone = File.read(name).split("\n__END__\n", 2) if postpone File.open(name, 'w') { |fd| fd.puts postpone } else File.unlink(name) end begin instance_eval script if script rescue Exception puts "script #{name} error: #$!", $!.backtrace end profil if curlogin rescue nil end end def self.bot_start(unused=nil) $stdout.sync = true m = new if Process.getrlimit(Process::RLIMIT_AS)[0] < Process::RLIM_INFINITY m.http_client.do_cache = false m.http_client.cache['/mountyhall/MH_Play/Play_action.php'] = '42' m.http_client.cache['/mountyhall/MH_Play/Play_news.php'] = '28' end m.bot_start m end def bot_start puts '-'*80, ' -- '+Time.now.strftime('%d/%m %H:%M:%S') run_script 'todo_pre' login readall_msg(unreadmsg) if unreadmsg > 0 puts self case Mounty.dice(100) when 1, 2, 3, 4; bm when 5, 6, 7, 8; equipement when 9; petit_equipement when 10; mouches end run_script 'todo_now' run_script 'todo' if self[:pa] == 6 v = vue(5) v.lieux.delete_if { |e| e.nom =~ /Trou de M.t.orite/i and e.n != self[:n] } puts v end def sleep len, mot=nil if len > 1200 and ARGV.include? 'oneshot' puts 'oneshot - exiting' system 'ruby update.rb put log_mh' if File.exist? 'update.rb' exit elsif len > 120 puts "[sleep #{mot} #{len/3600}h#{(len % 3600) / 60} => #{(now + len).strftime('%H:%M:%S')}]" system 'ruby update.rb put log_mh' if File.exist? 'update.rb' GC.start end super(len) end # sleep_until '12h48' def sleep_until(h, mot=nil) h, m, s = case h when /^(\d+):(\d+):(\d+)$/; [$1, $2, $3] when /^(\d+)[:h](\d+)$/; [$1, $2, rand(40)] when /^(\d+)h$/; [$1, rand(5), rand(40)] end.map { |i| i.to_i } nw = Time.now delay = Time.mktime(nw.year, nw.month, nw.day, h, m, s) - nw delay += 24*3600 if delay < -60 sleep delay.to_i, mot if delay > 0 end def reexec system 'ruby update.rb update' if File.exist? 'update.rb' exec "ruby #$0 #{ARGV * ' '}" end def bot_waitcumul run_script 'todo_cumul' puts '', self dla = self[:dla] dla -= Mounty.dice(20*60, 3) delay = dla.to_i - now.to_i if delay > 14*3600 puts 'safety early wakeup' dla = now + 3600*8 + Mounty.dice(3600, 3*2) elsif delay > 8*3600 and Mounty.dice(4) == 1 puts 'random early wakeup' dla = now + 3600*5 + Mounty.dice(3600, 3) end dla -= Mounty.dice(3600/3, 3*2) while dla.hour < 6 and dla.hour > 1 # jamais entre 2h00 et 5h59 dla -= Mounty.dice(3600/3, 3*2) if dla.hour == 1 and (dla - now < 3*3600 or Mounty.dice(3) != 1) # rarement entre 1h et 2h dla -= Mounty.dice(3600/3, 3*2) if dla.hour < 6 and (dla - now < 3*3600 or Mounty.dice(3) != 1) # peu entre 0h et 2h (cumulatif avec 1-2h) # check Time.now pour eviter il est 16h, dla 4h => sleep til 23h => resleep til 1h delay = dla.to_i - now.to_i if delay > 3*3600 logout @http_client.clear GC.start sleep delay, 'cumul' reexec end run_script 'todo_postcumul' end def bot_stop run_script 'todo_end' bot_goinfrequip if Mounty.dice(6) == 1 logout @http_client.clear GC.start puts '', self dla = self[:dla] if dla > now and dla < now + 600 dla += Mounty.dice(15, 6) elsif dla > now dla += Mounty.dice(10*60) dla += Mounty.dice(10*60, 5) if Mounty.dice(4) == 1 delay = dla.to_i - now.to_i if delay > 14*3600 puts 'safety early wakeup' dla = now + 3600*6 + Mounty.dice(3600, 2*2) end dla += Mounty.dice(3600/3, 3*2) if dla.hour == 2 and Mounty.dice(5) != 1 dla += Mounty.dice(3600/3, 3*2) while dla.hour > 2 and dla.hour < 7 end delay = dla.to_i - now.to_i if delay > 0 delay += Mounty.dice(3600, 2) if delay > 7200 and Mounty.dice(3) == 1 sleep delay, 'nextdla' end reexec end def bot_goinfre(listfile = 'goinfrlist') goinfrlist = (File.read(listfile).split.map { |i| Integer(i) } rescue []).uniq return if goinfrlist.empty? profil ppa = @status[:pa] goinfre goinfrlist.first profil if @status[:pa] != ppa or ppa >= 4 goinfrlist.shift File.open(listfile, 'w') { |fd| fd.puts goinfrlist.join(' ') } return bot_goinfre(listfile) if @status[:pa] == ppa and ppa >= 4 true end end def bot_idt(id) r = idt id return r unless r and r.reussi and r.text.grep(/^(\d+) - (.*) \(/).first =~ /^(\d+) - (.*) \(/ id, name = $1, $2 bot_mayaddgoinfre id, name r end def bot_goinfrequip equipement[1].each { |id, name, etat, caracts| bot_mayaddgoinfre(id, name) } end def bot_mayaddgoinfre(id, name) # XXX wantidt ? (le meneur doit etre sous l'effet de..) if File.read('badidt').split("\n").map { |s| s.downcase }.include? name.downcase or name =~ /usage unique/ goinfrlist = (File.read('goinfrlist').split.map { |i| Integer(i) } rescue []).uniq goinfrlist.delete id.to_i goinfrlist.unshift id.to_i puts "#{name.inspect}: a goinfrer !" File.open('goinfrlist', 'w') { |fd| fd.puts goinfrlist.join(' ') } profil bot_goinfre if self[:pa] >= 1 end end def bot_gettarget system 'ruby update.rb update' if File.exist? 'update.rb' targetid, targethit, targetesq, targetrm, targetarm, targetsubj = File.read('targetid').chomp.split(';', 6).map { |e| (Integer(e) rescue e) if e and not e.empty? } targetsubj = nil if targetsubj and targetsubj.empty? target = vue.anim.find { |e| e.id == targetid } target.hit, target.esq, target.rm, target.arm, target['subj'] = targethit, targetesq, targetrm, targetarm, targetsubj if target target end def bot_settarget(t) t = "#{t.id};#{t.hit};#{t.esq};#{t.rm};#{t.arm};#{t['subj']}" if t != -1 File.open('targetid', 'w') { |fd| fd.puts t } system 'ruby update.rb yell targetid' if File.exist? 'update.rb' end def bot_setdest(x, y=nil, z=nil) x, y, z = x if x.kind_of? ::Array File.open('dest', 'w') { |fd| fd.puts "#{x}, #{y}, #{z}" } system 'ruby update.rb yell dest' if File.exist? 'update.rb' end # accepte un bloc pour un hypno, si il renvoie true l'hypno est lancee def bot_attaque(target=bot_gettarget, canmove=true) case target when Integer; return if not target = vue.anim.find { |a| a.id == target } || vue.anim.find { |a| a.id.to_s =~ /#{target}/ } when String; return if not target = vue.anim.find { |a| a.nom =~ /#{target}/i } end return if not target or target.n == 0 # stats: esperance de degats en fonction de att/esq, deg/arm, mm/rm par comp # maitrise comp n'intervient pas, empecherait de monter de nouvelles comps XXX ignorer sr pour monter sa mm ? # [esp, comp], comp == string (sortable) # XXX piege, CA ? esp_deg = [] add_esp = proc { |comp, datt, bmatt, ddeg, bmdeg, ddeg_bonus_crit, sr| # XXX formal approach ? esp = 0.0 n = 30 mm = self[:mm] + self[:bm_mm] rm = target.rm || self[:lvl] * 25 sr = [[((rm > mm) ? (100 - 50.0*mm/rm) : (50.0*rm/mm)), 90].min, 10].max if sr n.times { dsr = 1 if sr and Mounty.dice(100) <= sr dsr = 2 end att = Mounty.dice(6, datt) + bmatt esq = target.esq || Mounty.dice(6, self[:lvl]/3).to_i esp += \ if att <= esq / 2 0 elsif att <= esq 0.1 elsif att > esq*2 Mounty.dice(3, ddeg+ddeg_bonus_crit) + bmdeg elsif att > esq Mounty.dice(3, ddeg) + bmdeg end / dsr } puts "dbg b_att: comp #{comp} datt #{datt} bmatt #{bmatt} ddeg #{ddeg} bmdeg #{bmdeg} ddegcrit #{ddeg_bonus_crit}#{' sr %.2f' % sr if sr} => #{'%.2f' % (esp/n)} pv" esp_deg << [esp/n, comp] } if self[:pa] >= 4 and target.dist == 0 if self['butoir'] add_esp['cdb', self[:att], self[:bm_att]+self[:bmm_att], self[:deg] + [self[:deg]/2, self['butoir'][0]*3].min , self[:bm_deg]+self[:bmm_deg] - target.arm.to_i, self[:deg]/2, false] end if self['pr.*cise'] add_esp['ap', self[:att] + [self[:att]/2, self['pr.*cise'][0]*3].min, self[:bm_att]+self[:bmm_att], self[:deg], self[:bm_deg]+self[:bmm_deg] - target.arm.to_i, self[:deg]/2, false] end if self[:pa] >= 6 and self['fr.*n.*sie'] # XXX ResultCombat * 2 ? add_esp['frenesie', self[:att], self[:bm_att]+self[:bmm_att]+2, self[:deg]*2, (self[:bm_deg]+self[:bmm_deg] - target.arm.to_i)*2, self[:deg]/2*2, false] # att+2 =~ malus esq sur la 2e end add_esp['attaque', self[:att], self[:bm_att]+self[:bmm_att], self[:deg], self[:bm_deg]+self[:bmm_deg] - target.arm.to_i, self[:deg]/2, false] end if self[:pa] >= 4 and self['charge'] and target.n == self[:n] and target.dist <= chargedist and target.dist > 0 add_esp['charge', self[:att], self[:bm_att]+self[:bmm_att], self[:deg], self[:bm_deg]+self[:bmm_deg] - target.arm.to_i, self[:deg]/2, false] end if self['projectile magique'] and not self[:tried_sort] and self[:pa] >= 4 and target.dist <= projodist and target.n == self[:n] add_esp['projo', self[:vue], self[:bmm_att], self[:vue]/2, self[:bmm_deg], self[:vue]/4, true] end if self['botte secr.*te'] and not self[:tried_comp] and self[:pa] >= 2 and target.dist == 0 if target.esq add_esp['bs', self[:att]/2, self[:bm_att]/2+self[:bmm_att]/2, self[:att]/2, self[:bm_deg]/2+self[:bmm_deg]/2 - target.arm.to_i/2, self[:att]/4, false] else esp_deg << [3, 'bs'] end end if self['hypnotisme'] and not self[:tried_sort] and self[:pa] >= 4 and target.dist == 0 # hypno si bs non touche if (block_given? and yield) or (not block_given? and (target.esq || 10000) > (self[:att]*7/2 + self[:bm_att]/2 + self[:bmm_att]/2)/2 + 3) add_esp['hypno', 10000, 10000, 10000, 10000, 10000, true] unless target.esq and target.esq <= 3 end end if self['vampirisme'] and not self[:tried_sort] and self[:pa] >= 4 and target.dist == 0 # add fake bm_deg = pv gagnables add_esp['vampi', self[:deg]*2/3, self[:bmm_att], self[:deg], self[:bmm_deg] + [self[:pvmax]-self[:pv], self[:deg]].min, self[:deg]/2, true] end if self['rafale'] and not self[:tried_sort] and self[:pa] >= 4 and target.dist == 0 add_esp['rafale', 0, 100000, self[:deg], self[:bmm_deg], 0, true] end comp = esp_deg.sort.last if canmove.kind_of? String or canmove.kind_of? Symbol comp = esp_deg.find { |ed, cp| cp.to_s == canmove.to_s } force = true canmove = false end if not comp or (comp.first <= 2 and not force) if canmove dmin = 0 dmin = chargedist if self['charge'] and chargedist > dmin dmin = projodist if self['projectile'] and projodist > dmin dmin -= 1 if target.dist == dmin and dmin > 0 move_to_dest target.pos, dmin nil end else return if not r = send(comp.last, target.id) if r.kill if teamlist.length > 1 allpx = r.px-2 if allpx > teamlist.length and teamlist.length > 1 don_px(allpx, teamlist) end if false # old individual with noise levelling px = allpx / teamlist.length bonus = teamlist.dup bonus.delete_at(rand(bonus.length)) while bonus.length > allpx - px*teamlist.length teamlist(false).each { |i| ipx = px ipx += 1 if bonus.include? i don_px(ipx, i) if ipx > 0 } end end team_message('kill', target, r) bot_settarget -1 puts vue(1) return r end if r.deg target.hit ||= 0 target.hit += r.deg if not r.sr arm = r.degnoarm - r.deg arm = 0 if arm < 0 arm /= 2 if comp.last == 'frenesie' arm *= 2 if comp.last == 'bs' target.arm = arm if not target.arm or arm > target.arm or r.deg > 1 end end target.rm = r.rm if r.rm and (not target.rm or (r.sr > 10 and r.sr < 90)) # TODO sr == 10 and rm > t.rm .. target.esq = r.esq if r.esq target.esq -= 3 if r.esq and (not r.att or r.att > r.esq/2) if r.sr and not r.esq # XXX hypno target.hit ||= 0 if target.esq target.esq -= (self[:esq] * (r.jetsr > r.sr ? 1.5 : 1.0/3) * 3.5).round else target.esq = 0 end puts " hypno: new esq #{target.esq} (-#{(self[:esq] * (r.jetsr > r.sr ? 1.5 : 1.0/3) * 3.5).round})" end target.esq = 0 if target.esq and target.esq < 0 if r.deg team_message('hit', target, r) if rand(2) == 0 elsif r.sr team_message('hyp', target, r) else team_message('miss', target, r) if rand(3) == 0 end bot_settarget target # team_msg change target['subj'] r end end def team_message(cat, tg, stat=nil) rnd = proc { |*a| a[rand(a.length)] } mutate = proc { |s| s.capitalize! if rand(20) == 0 s.upcase! if s.length < 40 and rand(4) == 0 s[rand(s.length), 1] = '' if s.length > 8 and rand(40) == 0 rs = rand(s.length-1) s[rs, 2] = s[rs, 2].reverse if rand(20) == 0 rs = rand(s.length) s[rs, 1] = s[rs, 1]*2 if rand(40) == 0 s } ignoreprob = 1 art = proc { |n| fem = true if n =~ /peine|amibe|araign..?e|banshee|chauve-souris|chim..?re|coccicruelle|cockatrice|erinye|gargouille|gorgone|harpie|limace|mant|marili|vermine|ombre|plante|sorci..?re|strige|succube|vouivre/i rnd[(%w[A E I O U].include?(n[0, 1].upcase) ? "l'" : fem ? 'la ' : 'le '), (fem ? 'une ' : 'un ')] + n } tt = art[tg.nom] tt = rnd['n.'+tg.id.to_s, tt, tt[/.*(?= \[)/], tt[/.*(?= \[)/], tt[/.*(?= \[)/], 'id '+tg.id.to_s[-4..-1]] if tg['subj'] subj = 'Re : ' + tg['subj'] else subj = tt end case cat when 'pos' ignoreprob = 6 pos = tg.pos.map { |p| if rand(15) == 0; p+rand(3)-1 elsif rand(5) == 0 and p < 0; -p else p end } txt = '' txt << rnd["je suit #{tt}", "je vois #{tt}", tt, tt, "target #{tt} aquired"] if rand(3) == 0 txt << rnd["il est en ", "il va vers ", "parti en ", "je le vois en ", "en ", "l'est la : ", "position ", "a "] << pos.join(rnd[' ', ', ', ',', ' ', '/']) txt << ".\n" << rnd['Courrons !', 'On l\'aura', 'allons y', 'tayau', 'tayoo', 'chaaaaaaargez !'] if rand(5) == 0 when 'target' ignoreprob = 8 subj = rnd[tt, tt, "suivant: #{tt}", "prochain: #{tt}", "#{tt} ensuite", "sur #{tt}", "on bouffe #{tt}", "prochaine cible: #{tt}"] txt = rnd["allez lui dire bonjour", "a table !", "oki ?", "go go go", "zou", "hop", "go", "a l'assaut !", "a l'attaque !", "au boulot"] pos = tg.pos.map { |p| if rand(25) == 0; p+rand(3)-1 elsif rand(25) == 0 and p < 0; -p else p end } txt << rnd["\n", "\n\n"] << rnd["", " "] txt << rnd["il est en ", "en poste en ", "je le vois en ", "en ", "sur ", "tous en ", "vers "] << pos.join(rnd[' ', ', ', ',', ' ', '/']) when 'hit' ignoreprob = 4 txt = rnd["-#{stat.deg}pv, esquive: #{stat.esq}", "touch\xe9 !\n"+stat.text.grep(/points de|critiq/i).join("\n"), stat.text.join("\n"), "#{stat.deg}pv"] when 'hyp' ignoreprob = 8 txt = rnd["il est hypno", "hypno ok", "esquive reduite", stat.text.join("\n"), "esquive reduite a #{tg.esq}"] when 'miss' ignoreprob = 2 txt = rnd["foir\xe9", "vive les PA dans le vide", "ca sera pour un autre jour", "et un tour dans le vide", "je passe", "same player shoots again", "grrrrrrr", "grrr", "raah", "pffffffff", "pffff"] txt += "\n\n" + stat.text.join("\n") if rand(6) != 0 when 'kill' ignoreprob = 4 subj = rnd["#{tt} ko", "#{tt} est mort ce soir", "#{tt} out", "#{subj} [fini]"] if rand(3) == 0 txt = rnd["au suivant !", "suivant", "hasta la vista", "l'a rejoint ses ancetres", stat.text[3..-3].join("\n"), stat.text[4..-1].join("\n"), stat.text.join("\n"), stat.text[3..-1].join("\n"), "il lui restait a peine #{stat.deg}pv"] end destlist = teamlist(false) destlist.delete_at(rand(destlist.length)) if rand(5) == 0 # reply // reply all if destlist.length > 0 and rand(ignoreprob) != 0 and rand(3) == 1 subj ||= 'attaque' if not tg['subj'] tg['subj'] = mutate[subj] else tg['subj'] = subj end message_send destlist, tg['subj'], mutate[txt] end end def achat_taniere(list=nil) if @http_client.cur_url !~ /Lieu_TaniereAchat.php/ menu_get '/mountyhall/MH_Lieux/Lieu_Description.php' get '/mountyhall/MH_Lieux/Lieu_TaniereAchat.php' end if list list = [list] if not list.kind_of? Array pd = @http_client.post_allowed.first pd.vars.delete_if { |k, v| k.include? 'Select' and not list.find { |t| k.include? t.to_s } } post pd.url, pd.vars show get_confirm else show_table_content(nil, false) end end def achat_maha(id=nil) if @http_client.cur_url !~ /Lieu_Achat.php/ menu_get '/mountyhall/MH_Lieux/Lieu_Description.php' get '/mountyhall/MH_Lieux/Lieu_Achat.php' end if id get "Lieu_Actions/Lieux_a_Acheter.php?as_Action=Acheter&ai_IDTarget=#{id}&ai_IDLieu=2" show get_confirm else show_table_content(nil, false) end end def self.inscription1(captchafile = '/tmp/captcha.jpg') @inscription_h = HttpClient.new('www.mountyhall.com') @inscription_h.get '/' @inscription_h.get '/Inscription.php' File.open(captchafile, 'w') { |fd| fd.write @inscription_h.cache['/codeImage/codeImage.php'].content } puts 'captcha saved as ' + captchafile puts 'call inscription2(nom, mail, raceid, captcha) -- race: 1skrim 2tom 4dudu 5kastar' @inscription_h end def self.inscription2(nom, mail, race, captcha) raise 'bad race' if not %w[1 2 4 5].include? race.to_s pd = @inscription_h.post_allowed.first pd.vars.delete 'ab_ShowEmail' pd.vars.update 'as_NickName' => nom, 'ai_Race' => race.to_s, 'as_Email' => mail, 'as_codeValidation' => captcha @inscription_h.post pd.url, pd.vars puts @inscription_h.curpage.get_text end def set_mail_options options get 'Options/Play_o_MailOption.php' pd = http_client.post_allowed.first post pd.url, 'ab_SendMail1' => '1', 'ab_SendMail2' => '1', 'ab_SendMail4' => '1', 'as_Action' => pd.vars['as_Action'] profil end end