#!/usr/bin/wish # show your vessels on a map # This is part of ypp-sc-tools, a set of third-party tools for assisting # players of Yohoho Puzzle Pirates. # # Copyright (C) 2009 Ian Jackson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Yohoho and Puzzle Pirates are probably trademarks of Three Rings and # are used without permission. This program is not endorsed or # sponsored by Three Rings. source yarrglib.tcl source panner.tcl package require http #---------- general utilities ---------- set debug 0 proc debug {m} { global debug if {$debug} { puts "DEBUG $m" } } proc badusage {m} { puts stderr "where-vessels: bad usage: $m" exit 1 } proc glset {n val} { upvar #0 $n var set var $val } #---------- expecting certain errors ---------- proc errexpect-setline {lno line} { glset errexpect_lno $lno glset errexpect_line $line } proc errexpect-error {m} { global errexpect_line errexpect_lno error $m "$errexpect_line\n" [list YARRG-ERREXPECT $errexpect_lno] } proc errexpect-arrayget {arrayvar key} { upvar 1 $arrayvar av debug "VARS [array names av]" upvar 1 ${arrayvar}($key) v if {[info exists v]} { return $v } errexpect-error "undefined $key" } proc errexpect-catch {code} { global errorInfo errorCode set rc [catch { uplevel 1 $code } rv] debug "ERREXPECT CATCH |$rc|$rv|$errorCode|$errorInfo|" if {$rc==1 && ![string compare YARRG-ERREXPECT [lindex $errorCode 0]]} { return [list 1 $rv [lindex $errorCode 1] $errorInfo] } elseif {$rc==0} { return [list 0 $rv] } else { return -code $rc -errorinfo $errorInfo -errorcode $errorCode $rv } } #---------- argument parsing ---------- proc nextarg {} { global ai argv if {$ai >= [llength $argv]} { badusage "option [lindex $argv [expr {$ai-1}]] needs a value" } set v [lindex $argv $ai] incr ai return $v } set notes_loc vessel-notes set scraper {./yppedia-ocean-scraper --chart} proc parseargs {} { global ai argv global debug scraper set ai 0 while {[regexp {^\-} [set arg [lindex $argv $ai]]]} { incr ai switch -exact -- $arg { -- { break } --pirate { glset pirate [string totitle [nextarg]] } --ocean { glset ocean [string totitle [nextarg]] } --clipboard-file { load-clipboard-file [nextarg] } --local-html-dir { lappend scraper --local-html-dir=[nextarg] } --notes { glset notes_loc [nextarg] } --debug { incr debug } default { badusage "unknown option $arg" } } } set argv [lrange $argv $ai end] if {[llength $argv]} { badusage "non-option args not allowed" } } proc argdefaults {} { global ocean notes_loc pirate scraper if {![info exists ocean] || ![info exists pirate]} { set cmd {./yarrg --find-window-only --quiet} if {[info exists ocean]} { lappend cmd --ocean $ocean } if {[info exists pirate]} { lappend cmd --pirate $pirate } manyset [split [eval exec $cmd] " "] ocean pirate } lappend scraper $ocean } #---------- loading and parsing the vessel notes ---------- proc load-notes {} { global notes_loc notes catch { unset notes } if {[regexp {^\w+\:} $notes_loc]} { vwait idletasks debug "FETCHING NOTES" ::http::geturl $notes_loc switch -glob [::http::status].[::http::ncode] { ok.200 { } ok.* { error "retrieving vessel-notes $url: [::http::code]" } * { error "retrieving vessel-notes $url: [::http::error]" } } set notes_data [::http::data] ::http::cleanup } else { set vn [open $notes_loc] set notes_data [read $vn] close $vn } foreach l [split $notes_data "\n"] { regsub -all {\t+} $l "\t" l manyset [split $l "\t"] vname vid owner note set nk $vid.$vname debug "SET NOTE $nk" set notes($nk) [list $owner $note] } } #---------- loading and parsing the clipboard (vessel locations) ---------- proc vessel {vin} { global pirate notes_used upvar 1 $vin vi switch -exact [errexpect-arrayget vi vesselClass] { smsloop { set sz 00sl } lgsloop { set sz 01ct } dhow { set sz 02dh } longship { set sz 03ls } baghlah { set sz 04bg } merchbrig { set sz 05mb } warbrig { set sz 06wb } xebec { set sz 07xe } warfrig { set sz 08wf } merchgal { set sz 09mg } grandfrig { set sz 10gf } default { errexpect-error "unknown class" } } set abbrev $sz switch -exact [errexpect-arrayget vi vesselSubclass] { null { } icy { append abbrev F } default { errexpect-error "unknown subclass ?" } } switch -exact [errexpect-arrayget vi isLocked]/[ \ errexpect-arrayget vi isBattleReady] { true/false { append abbrev 2- } false/false { append abbrev 1+ } false/true { append abbrev 0* } default { errexpect-error "unexpected isLocked/isBattleReady" } } switch -exact [errexpect-arrayget vi inPort] { true { } false { append abbrev ? } default { errexpect-error "unexpected inPort" } } set nk [errexpect-arrayget vi vesselId].[errexpect-arrayget vi vesselName] upvar #0 notes($nk) note if {[info exists note]} { manyset $note owner xabbrev if {![string compare $owner $pirate]} { append abbrev = } else { append abbrev - } append abbrev $xabbrev set notes_used($nk) 1 } else { debug "UNKNOWN $nk" } set kk "[errexpect-arrayget vi islandName] $abbrev" upvar #0 count($kk) k if {![info exists k]} { set k 0 } incr k } set clipboard {} proc parse-clipboard {} { global clipboard count notes notes_used catch { unset count } catch { unset notes_used } set itemre { (\w+) = ([^=]*) } set manyitemre "^\\\[ $itemre ( (?: ,\\ $itemre)* ) \\]\$" debug $manyitemre set lno 0 foreach l [split $clipboard "\n"] { incr lno errexpect-setline $lno $l if {![string length $l]} continue catch { unset vi } while 1 { if {![regexp -expanded $manyitemre $l dummy \ thiskey thisval rhs]} { errexpect-error "badly formatted" } set vi($thiskey) $thisval if {![string length $rhs]} break regsub {^, } $rhs {} rhs set l "\[$rhs\]" } vessel vi } foreach nk [lsort [array names notes]] { if {![info exists notes_used($nk)]} { debug "IGNORED NOTE $nk" } } } proc load-clipboard-file {fn} { set f [open $fn] glset clipboard [read $f] close $f } #---------- loading and parsing the chart ---------- proc load-chart {} { global chart scraper debug "FETCHING CHART" set chart [eval exec $scraper [list | perl -we { use strict; use CommodsScrape; use IO::File; use IO::Handle; yppedia_chart_parse(\*STDIN, (new IO::File ">/dev/null"), sub { sprintf "%d %d", @_; }, sub { printf "archlabel %d %d %s\n", @_; }, sub { printf "island %s %s\n", @_; }, sub { printf "league %s %s %s.\n", @_; }, sub { printf STDERR "warning: %s: incomprehensible: %s", @_; } ); STDOUT->error and die $!; }]] } set scale 16 proc coord {c} { global scale return [expr {$c * $scale}] } proc chart-got/archlabel {args} { } proc chart-got/island {x y args} { # debug "ISLE $x $y $args" global canvas isleloc set isleloc($args) [list $x $y] set sz 5 # $canvas create oval \ # [expr {[coord $x] - $sz}] [expr {[coord $y] - $sz}] \ # [expr {[coord $x] + $sz}] [expr {[coord $y] + $sz}] \ # -fill blue $canvas create text [coord $x] [coord $y] \ -text $args -anchor s } proc chart-got/league {x1 y1 x2 y2 kind} { # debug "LEAGUE $x1 $y1 $x2 $y2 $kind" global canvas set l [$canvas create line \ [coord $x1] [coord $y1] \ [coord $x2] [coord $y2]] if {![string compare $kind .]} { $canvas itemconfigure $l -dash . } } proc draw {} { global chart count isleloc canvas $canvas delete all foreach l [split $chart "\n"] { # debug "CHART-GOT $l" set proc [lindex $l 0] eval chart-got/$proc [lrange $l 1 end] } set lastislandname {} foreach key [lsort [array names count]] { set c $count($key) # debug "SHOWING $key $c" regexp {^(.*) (\S+)$} $key dummy islandname abbrev if {[string compare $lastislandname $islandname]} { manyset $isleloc($islandname) x y set x [coord $x] set y [coord $y] set lastislandname $islandname # debug "START Y $y" } set text $abbrev regsub -all {[0-9]} $text {} text if {$c > 1} { set text [format "%2d%s" $c $text] } else { set text [format " %s" $text] } set id [$canvas create text $x $y \ -anchor nw -font fixed \ -text $text] set bbox [$canvas bbox $id] set bid [eval $canvas create rectangle $bbox -fill white] $canvas lower $bid $id manyset $bbox dummy dummy dummy y # debug "NEW Y $y" } panner::updatecanvas-bbox .ctrl.pan } #---------- user interface ---------- proc widgets-setup {} { global canvas debug acqdeffont frame .f -border 1 -relief groove set canvas .f.c canvas $canvas pack $canvas -expand 1 -fill both pack .f -expand 1 -fill both -side left frame .ctrl pack .ctrl -side right debug "BBOX [$canvas bbox all]" panner::canvas-scroll-bbox .f.c panner::create .ctrl.pan .f.c 120 120 $debug pack .ctrl.pan -side top -pady 10 -padx 5 frame .ctrl.zoom pack .ctrl.zoom -side top button .ctrl.zoom.out -text - -font {Courier 16} -command {zoom /2} button .ctrl.zoom.in -text + -font {Courier 16} -command {zoom *2} pack .ctrl.zoom.out .ctrl.zoom.in -side left frame .ctrl.acquire button .ctrl.acquire.do -text Acquire -command acquire frame .ctrl.acquire.resframe -width 120 -height 32 button .ctrl.acquire.resframe.res -text {} -anchor nw \ -padx 1 -pady 1 -borderwidth 0 -justify left glset acqdeffont [.ctrl.acquire.resframe.res cget -font] place .ctrl.acquire.resframe.res -relx 0.5 -y 0 -anchor n # -relheight 1.0 -relwidth 1.0 pack .ctrl.acquire.do -side top pack .ctrl.acquire.resframe -side top -expand y -fill both pack .ctrl.acquire -side top toplevel .err_acquire wm withdraw .err_acquire wm title .err_acquire "where-vessels clipboard parsing error" label .err_acquire.title -text "Clipboard parsing error" pack .err_acquire.title -side top button .err_acquire.close -text Close -command {wm withdraw .err_acquire} pack .err_acquire.close -side bottom frame .err_acquire.emsg -bd 2 -relief groove label .err_acquire.emsg.lab -text "Error:" text .err_acquire.emsg.text -height 1 pack .err_acquire.emsg.text -side bottom pack .err_acquire.emsg.lab -side left pack .err_acquire.emsg -side top -pady 2 frame .err_acquire.text -bd 2 -relief groove pack .err_acquire.text -side bottom -pady 2 label .err_acquire.text.lab -text "Clipboard contents:" text .err_acquire.text.text \ -xscrollcommand {.err_acquire.text.xscroll set} \ -yscrollcommand {.err_acquire.text.yscroll set} scrollbar .err_acquire.text.xscroll -orient horizontal \ -command {.err_acquire.text.text xview} scrollbar .err_acquire.text.yscroll -orient vertical \ -command {.err_acquire.text.text yview} grid configure .err_acquire.text.lab -row 0 -column 0 -sticky w grid configure .err_acquire.text.text -row 1 -column 0 grid configure .err_acquire.text.yscroll -sticky ns -row 1 -column 1 grid configure .err_acquire.text.xscroll -sticky ew -row 2 -column 0 wm geometry . 1024x480 } proc zoom {extail} { global scale canvas set nscale [expr "\$scale $extail"] debug "ZOOM $scale $nscale" if {$nscale < 1 || $nscale > 200} return set scale $nscale draw } proc acquire_showerror {} { global acqerr tk_messageBox -type ok \ -title "where-vessels: clipboard parsing error" \ -message $acqerr } proc acquire {} { global clipboard acqdeffont acqerr errorInfo set old $clipboard set nclipboard [clipboard get] manyset [errexpect-catch { set clipboard $nclipboard parse-clipboard .ctrl.acquire.resframe.res configure \ -background blue -disabledforeground black -font $acqdeffont \ -state disabled -command {} \ -text " acquired ok " }] failed emsg lno ei if {$failed} { set line [lindex [split $ei "\n"] 0] puts stderr "clipboard parsing failed: line $lno: $emsg\n $line" regsub -all {.{18}} "bad: [string trim $emsg]: \"$line\"" "&\n" ewrap .err_acquire.emsg.text delete 0.0 end .err_acquire.emsg.text insert end "at line $lno: $emsg" .err_acquire.text.text delete 0.0 end .err_acquire.text.text insert end $nclipboard .err_acquire.text.text tag add error $lno.0 $lno.end .err_acquire.text.text tag configure error \ -background red -foreground white .err_acquire.text.text see $lno.0 .ctrl.acquire.resframe.res configure \ -background red -foreground white -font fixed \ -state normal -command {wm deiconify .err_acquire} \ -text $ewrap set clipboard $old parse-clipboard } draw } #---------- main program ---------- parseargs argdefaults httpclientsetup where-vessels load-chart widgets-setup load-notes parse-clipboard draw