#!/usr/bin/ruby # args: # nozero => vire le niveau 0 # split => affiche separement monstres/trolls/lieux # dmax42 => dmax 42 # script de vision 2D par script public pour MountyHall - par Tyrollus (johnatofjj.net) # plein de cache de partout # TODO: affichage 2d # formule de seuil de niveau en pi: s = 0 ; (0..50).each { |d| s += 10*(d+2) ; puts s } (20 50 90 140 200..) # formule de gain d'xp au kill: 11 + lvl + 3*(difflvl) # +1 si sortilege/parchemin # -1 si potion require 'libhttp' require 'libhtml' require 'mountycreds' # fichier mountycreds.rb: # class Mounty # @@login = '42' # @@pass = 'kikoo' # @@passr = '758azerkj2358serjazlkj235' # end class MHInfo @@files = ['Competences', 'Distinctions', 'Equipement', 'Guildes', 'Sortileges', 'Tresors', 'TresorsMagie', 'Trolls'] @@local_cache_pfx = '.mh_cache/file_' @@local_cache_duration = 24*3600 @@monstre = nil def initialize @httpserver = HttpServer.new('www.mountyhall.com') @monstre_httpserver = HttpServer.new('trolls.game-host.org') end def load_monstre_cache cachefile = @@local_cache_pfx + 'monstre_niveau' @@monstre = Hash.new begin File.readlines(cachefile).each { |l| s = l.chomp.split(';') @@monstre[s[0]] = s[1] } rescue Errno::ENOENT end end def save_monstre_cache cachefile = @@local_cache_pfx + 'monstre_niveau' File.open(cachefile, 'w') { |fd| @@monstre.each { |k, v| fd.puts "#{k};#{v}" } } end def monstre_niveau(nom) load_monstre_cache unless @@monstre return @@monstre[nom] if @@monstre[nom] # http://trolls.game-host.org/mountyhall/cdm.php?monstre=Ma%EEtre+Squelette+%5BNaissant%5D req = '/mountyhall/cdm.php?monstre=' req += @monstre_httpserver.urlenc nom raw = @monstre_httpserver.get(req).content p = parsehtml(raw) go = false p.each { |e| if go and e.type == 'String' e.attr['content'] =~ / (\d+)$/ @@monstre[nom] = $1 break end go = true if e.type == 'h1' } save_monstre_cache @@monstre[nom] end def get_file(base) raise "invalid base #{base.inspect}" unless @@files.include? base cachefile = @@local_cache_pfx + base if File.exists?(cachefile) and Time.now.to_i - File.stat(cachefile).mtime.to_i < @@local_cache_duration return File.read(cachefile) end req = '/mountyhall/ftp/Public_'+base+'.txt' raw = @httpserver.get(req).content File.open(cachefile, 'w') { |fd| fd.write raw } raw end def update_cache @@files.each { |f| get_file(f) } end def parse #** Public_Trolls.txt ** #Id ; Nom ; Race ; Niveau ; Nb de Kills ; Nb de Morts ; Id Guilde ; Nb de Mouches #** Publics_Guildes.txt ** #Id ; Nom ; Nb Membres #** Public_Sortileges.txt ** #Id ; Nom ; Type ; Cout PA ; Duree ; Usage RM (*) ; Usage en Surface (*) ; Effet de Zone (*) #** Public_Competences.txt ** #Id ; Nom ; Type ; Cout PA ; Pourcentage Base ; Niveau Minimum #** Public_Tresors.txt ** #Id ; Nom ; Type #** Public_TresorsMagie.txt ** #Id ; Nom #** Public_Distinctions.txt ** #Id Troll ; Nom #** Public_Equipement.txt ** #Id ; Id Troll ; Nom ; Magie ; Type ; Identification (*); #(*) 1 : Oui, 0 : Non end end class Array def shrink(sz, elem, sep) # reduit le tableau a sz length en mergeant elem avec elem+1 while length > sz self[elem] << sep << delete_at(elem+1) end self end end class MHElem attr_reader :attrs, :cat attr_reader :id, :x, :y, :n attr_accessor :nom, :guilde, :lvl attr_accessor :d def initialize(cat, s) @cat = cat @attrs = s.split ';' @nom = @guilde = @lvl = nil if @attrs.length == 4 @id, @x, @y, @n = *@attrs else @attrs.shrink(5, 1, ?;) @id, @nom, @x, @y, @n = *@attrs end end def trollinfo(i) #Id ; Nom ; Race ; Niveau ; Nb de Kills ; Nb de Morts ; Id Guilde ; Nb de Mouches @nom = i[1] @race = i[2] @lvl = i[3] @guilde = i[6] if i[6].to_i != 1 end @@lastx = @@lasty = @@lastn = 1 def to_s if @x == @@lastx and @y == @@lasty and @n == @@lastn s = " (#@id)" # "(1) en 000,000 000: ( else s = "(#@d) en %3d,%3d %3d: (#@id)" % [@x, @y, @n] @@lastx = @x @@lasty = @y @@lastn = @n end if @lvl s << ' ' s << (@race ? @race[0] : 'l.') s << @lvl end s << ' '+@nom if @nom s << " (#@guilde)" if @guilde s end def <=>(other) [:d, :n, :x, :y, :id].each { |f| diff = (send(f).to_i <=> other.send(f).to_i) return diff unless diff == 0 } (@cat + @nom) <=> (other.cat + other.nom) end end class MHVue < Mounty attr_reader :raw attr_reader :cat @@local_cache_pfx = '.mh_cache/vue_' @@local_cache_duration = 6*3600 def initialize(login = @@login, passr = @@passr) @login, @passr = login, passr @httpserver = HttpServer.new('sp.mountyhall.com') @cat = Hash.new end def get_profil cachefile = '.mh_cache/profil_' + @login if File.exists?(cachefile) and Time.now.to_i - File.stat(cachefile).mtime.to_i < @@local_cache_duration return @raw = File.read(cachefile) end puts 'connection au site pour le profil' req = "/SP_Profil.php?Numero=#{@login}&Motdepasse=#{@passr}" @raw = @httpserver.get(req).content File.open(cachefile, 'w') { |fd| fd.write @raw } @raw end def show_profil x = get_profil.chomp.split(';') # mat x y n pv pvmax pa dla datq desq ddeg dreg vue arm mm rm dla = x.delete_at(7) lbl = %w{mat x y n pv pvmax pa datq desq ddeg dreg vue arm mm rm} x.each { |v| puts "#{lbl.shift}: #{v.to_i}" } dla =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/ dla_t = Time.mktime($1, $2, $3, $4, $5, $6) delay = dla_t.to_i - Time.now.to_i puts "dla: #{dla} (dans #{delay/3600}h#{delay/3600 % 60}m#{delay%60})" end def get cachefile = @@local_cache_pfx + @login if File.exists?(cachefile) and Time.now.to_i - File.stat(cachefile).mtime.to_i < @@local_cache_duration return @raw = File.read(cachefile) end puts 'connection au site pour la vue' req = "/SP_Vue.php?Numero=#{@login}&Motdepasse=#{@passr}&Tresors=1&Lieux=1&Champignons=1" @raw = @httpserver.get(req).content File.open(cachefile, 'w') { |fd| fd.write @raw } @raw end def update get if @raw =~ /^Erreur/ raise "Notification d'erreur : "+@raw end cat = ar = nil @raw.each { |l| l.chomp! case l when /^#DEBUT (.*)/ cat = $1 ar = @cat[cat] = [] when /^#FIN/ ar = nil else ar << MHElem.new(cat, l) end } info = MHInfo.new sp, t = nil unk_trolls = @cat['TROLLS'].dup.sort_by { |t| t.id } info.get_file('Trolls').each { |t| sp = t.chomp.split(';') if sp[0] == unk_trolls[0].id sp.shrink(8, 1, ?;) unk_trolls.shift.trollinfo(sp) end while unk_trolls[0] and sp[0].to_i > unk_trolls[0].id.to_i unk_trolls.shift.nom = '' end break if unk_trolls.empty? } unk_guild = @cat['TROLLS'].map { |t| t.guilde.to_i if t.guilde }.compact.uniq.sort info.get_file('Guildes').each { |g| sp = g.chomp.split(';') if sp[0].to_i == unk_guild[0] sp.shrink(3, 1, ?;) @cat['TROLLS'].each { |t| t.guilde = sp[1] if t.guilde == sp[0] } unk_guild.shift end while not unk_guild.empty? and sp[0].to_i > unk_guild[0] unk_guild.shift end break if unk_guild.empty? } @cat['MONSTRES'].each { |m| m.lvl = info.monstre_niveau(m.nom) } nil end def update! cachefile = @@local_cache_pfx + @login File.unlink(cachefile) if File.exists?(cachefile) update end def show src = @cat['TROLLS'].find { |t| t.id == @login } if ARGV.include? 'split' @cat else { 'ALL' => @cat.values.flatten } end.each { |k, v| v.each { |e| if src e.d = [:x, :y, :n].map { |f| (src.send(f).to_i - e.send(f).to_i).abs }.inject { |a, b| a > b ? a : b } else e.n.to_i end } v.delete_if { |e| e.n == '0' } if ARGV.include? 'nozero' dmax = nil v.delete_if { |e| e.d > dmax } if dmax = ARGV.inject(500) { |a, e| e =~ /dmax(\d+)/ ? $1.to_i : a } puts "#{k}:", v.sort, '' unless v.empty? } end end #MHInfo.new.update_cache if ARGV.include? 'profil' MHVue.new.show_profil else v = MHVue.new v.update v.show end