--- deliantra/Deliantra-Client/bin/pclient 2006/04/06 21:09:15 1.5
+++ deliantra/Deliantra-Client/bin/pclient 2006/04/24 11:45:17 1.177
@@ -1,204 +1,1469 @@
#!/opt/bin/perl
use strict;
+use utf8;
-use Crossfire::Client;
+BEGIN {
+ if (%PAR::LibCache) {
+ @INC = grep ref, @INC; # weed out all paths except pars loader refs
+
+ while (my ($filename, $zip) = each %PAR::LibCache) {
+ for ($zip->memberNames) {
+ next unless /^\/root\/(.*)/;
+ $zip->extractMember ($_, "$ENV{PAR_TEMP}/$1")
+ unless -e "$ENV{PAR_TEMP}/$1";
+ }
+ }
+
+ unshift @INC, $ENV{PAR_TEMP};
+
+ if ($^O eq "MSWin32") {
+ $ENV{GTK_RC_FILES} = "$ENV{PAR_TEMP}/share/themes/MS-Windows/gtk-2.0/gtkrc";
+ }
+ }
+}
+
+# need to do it again because that pile of garbage called PAR nukes it before main
+unshift @INC, $ENV{PAR_TEMP};
+
+use Time::HiRes 'time';
+use Event;
+
+use Crossfire;
use Crossfire::Protocol;
-package Crossfire::Client; # uh, yeah
+use Compress::LZF;
-use strict;
+use CFClient;
+use CFClient::UI;
+use CFClient::MapWidget;
+
+$Event::DIED = sub {
+ CFClient::error $_[1];
+};
-use SDL;
-use SDL::App;
-use SDL::Event;
-use SDL::Surface;
-use SDL::OpenGL;
-use SDL::OpenGL::Constants;
+our $VERSION = '0.1';
-my $conn;
-my $app;
+my $MAX_FPS = 60;
+my $MIN_FPS = 5; # unused as of yet
-my $WIDTH = 640;
-my $HEIGHT = 480;
+our $META_SERVER = "crossfire.real-time.com:13326";
+
+our $FACEMAP;
+our $TILECACHE;
+our $MAPCACHE;
+
+our $LAST_REFRESH;
+our $NOW;
+
+our $CFG;
+our $CONN;
+our $FAST; # fast, low-quality mode, possibly useful for software-rendering
+
+our @SDL_MODES;
+our $WIDTH;
+our $HEIGHT;
+our $FULLSCREEN;
+our $FONTSIZE;
+
+our $FONT_PROP;
+our $FONT_FIXED;
+
+our $MAP;
+our $MAPWIDGET;
+our $BUTTONBAR;
+our $LOGVIEW;
+our $CONSOLE;
+our $METASERVER;
+
+our $FLOORBOX;
+our $GAUGES;
+our $STATWIDS;
+
+our $SDL_ACTIVE;
+our %SDL_CB;
+
+our $SDL_MIXER;
+our @SOUNDS; # event => file mapping
+our %AUDIO_CHUNKS; # audio files
+
+our $ALT_ENTER_MESSAGE;
+our $STATUS_LINE;
+our $DEBUG_STATUS;
+
+sub status {
+ $STATUS_LINE->set_text ($_[0]);
+ $STATUS_LINE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h} - $STATUS_LINE->{h});
+}
+
+sub debug {
+ $DEBUG_STATUS->set_text ($_[0]);
+ $DEBUG_STATUS->move ($WIDTH - $DEBUG_STATUS->{w}, 0, $DEBUG_STATUS->{w}, $DEBUG_STATUS->{h});
+}
-sub glinit {
- # nuke all gl context data
+sub start_game {
+ status "logging in...";
+
+ my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
+
+ $MAPCACHE = CFClient::db_table "mapcache_$CFG->{host}";
+
+ $MAP = new CFClient::Map $mapsize, $mapsize;
+
+ my ($host, $port) = split /:/, $CFG->{host};
- $app = new SDL::App
- -flags => SDL_ANYFORMAT | SDL_HWSURFACE,
- -title => "Crossfire+ Client",
- -width => $WIDTH,
- -height => $HEIGHT,
- -opengl => 1,
- -red_size => 8,
- -green_size => 8,
- -blue_size => 8,
- -double_buffer => 1,
- -resizeable => 0;
-
- glEnable GL_TEXTURE_2D;
- glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL;
- glShadeModel GL_FLAT;
- glDisable GL_DEPTH_TEST;
- glMatrixMode GL_PROJECTION;
+ $CONN = new conn
+ host => $host,
+ port => $port || 13327,
+ user => $CFG->{user},
+ pass => $CFG->{password},
+ mapw => $mapsize,
+ maph => $mapsize,
+ ;
- glLoadIdentity;
- glOrtho 0, $WIDTH / 32, $HEIGHT / 32, 0, -1 , 1;
+ status "login successful";
- # re-bind all textures
+ CFClient::lowdelay fileno $CONN->{fh};
}
-sub refresh {
- glClearColor 0.5, 0.5, 0.7, 0;
- glClear GL_COLOR_BUFFER_BIT;
+sub stop_game {
+ undef $CONN;
+}
- my $map = $conn->{map};
+sub client_setup {
+ my $dialog = new CFClient::UI::FancyFrame
+ title => "Client Setup",
+ child => (my $vbox = new CFClient::UI::VBox);
+ $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
+
+ $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode");
+ $table->add (1, 0, my $hbox = new CFClient::UI::HBox);
+
+ $hbox->add (my $mode_slider = new CFClient::UI::Slider expand => 1, req_w => 100, range => [$CFG->{sdl_mode}, 0, scalar @SDL_MODES, 1]);
+ $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999");
+
+ $mode_slider->connect (changed => sub {
+ my ($self, $value) = @_;
+
+ $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
+ $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]});
+ });
+ $mode_slider->emit (changed => $mode_slider->{range}[0]);
+
+ my $row = 1;
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fullscreen");
+ $table->add (1, $row++, new CFClient::UI::CheckBox
+ state => $CFG->{fullscreen},
+ tooltip => "Bring the client into fullscreen mode",
+ connect_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{fullscreen} = $value;
+ }
+ );
- for my $x (0 .. $conn->{mapw} - 1) {
- for my $y (0 .. $conn->{maph} - 1) {
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
+ $table->add (1, $row++, new CFClient::UI::CheckBox
+ state => $CFG->{fast},
+ tooltip => "Lower the visual quality considerably to speed up rendering.",
+ connect_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{fast} = $value;
+ }
+ );
- my $cell = $map->[$x][$y]
- or next;
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Map Scale");
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{map_scale}, 0.25, 2, 0.05],
+ tooltip => "Enlarge or shrink the displayed map",
+ connect_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{map_scale} = 0.05 * int $value / 0.05;
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War");
+ $table->add (1, $row++, new CFClient::UI::CheckBox
+ state => $CFG->{fow_enable},
+ tooltip => "Fog-of-War marks areas that cannot be seen by the player",
+ connect_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{fow_enable} = $value;
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Intensity");
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{fow_intensity}, 0, 1 + 0.001, 0.001],
+ tooltip => "The higher the intensity, the lighter the Fog-of-War color",
+ connect_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{fow_intensity} = $value;
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Smooth");
+ $table->add (1, $row++, new CFClient::UI::CheckBox
+ state => $CFG->{fow_smooth},
+ tooltip => "Smooth the Fog-of-War a bit to make it more realistic",
+ connect_changed => sub {
+ my ($self, $value) = @_;
+ $CFG->{fow_smooth} = $value;
+ status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFClient::GL_VERSION < 1.2;
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{gui_fontsize}, 0.5, 2, 0.1],
+ tooltip => "The font size used by most GUI elements",
+ connect_changed => sub {
+ $CFG->{gui_fontsize} = 0.1 * int $_[1] * 10;
+# $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Server Log Fontsize");
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{log_fontsize}, 0.5, 2, 0.1],
+ tooltip => "The font size used by the server log window only",
+ connect_changed => sub {
+ $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = 0.1 * int $_[1] * 10);
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Stats Fontsize");
+
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{stat_fontsize}, 0.5, 2, 0.1],
+ tooltip => "The font size used by the statistics window only",
+ connect_changed => sub {
+ $CFG->{stat_fontsize} = 0.1 * int $_[1] * 10;
+ &set_stats_window_fontsize;
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size");
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{gauge_size}, 0.2, 0.8, 0.02],
+ tooltip => "Adjust the size of the stats gauges at the bottom right",
+ connect_changed => sub {
+ $CFG->{gauge_size} = $_[1];
+ my $h = int $HEIGHT * $CFG->{gauge_size};
+ $GAUGES->{win}->set_size ($WIDTH, $h);
+ $GAUGES->{win}->move (0, $HEIGHT - $h);
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
+ $table->add (1, $row++, new CFClient::UI::Slider
+ range => [$CFG->{gauge_fontsize}, 0.5, 2.0, 0.1],
+ tooltip => "Adjusts the fontsize of the gauges at the bottom right",
+ connect_changed => sub {
+ $CFG->{gauge_fontsize} = 0.1 * int $_[1] * 10;
+ &set_gauge_window_fontsize;
+ }
+ );
+
+ $table->add (1, $row++, new CFClient::UI::Button
+ expand => 1, align => 0, text => "Apply",
+ tooltip => "Apply the video settings",
+ connect_activate => sub {
+ video_shutdown ();
+ video_init ();
+ }
+ );
+
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Audio Enable");
+ $table->add (1, $row++, new CFClient::UI::CheckBox
+ state => $CFG->{audio_enable},
+ tooltip => "If enabled, sound effects and music will be played. If disabled, no audio will be used and the soundcard will not be opened.",
+ connect_changed => sub {
+ $CFG->{audio_enable} = $_[1];
+ }
+ );
+# $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Effects Volume");
+# $table->add (1, 8, new CFClient::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], connect_changed => sub {
+# $CFG->{effects_volume} = $_[1];
+# });
+ $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Background Music");
+ $table->add (1, $row++, my $hbox = new CFClient::UI::HBox);
+ $hbox->add (new CFClient::UI::CheckBox
+ expand => 1, state => $CFG->{bgm_enable},
+ tooltip => "Enable background music playing",
+ connect_changed => sub {
+ $CFG->{bgm_enable} = $_[1];
+ }
+ );
+ $hbox->add (new CFClient::UI::Slider
+ expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0.1],
+ tooltip => "The volume of the background music",
+ connect_changed => sub {
+ $CFG->{bgm_volume} = $_[1];
+ CFClient::MixMusic::volume $_[1] * 128;
+ }
+ );
+
+ $table->add (1, $row++, new CFClient::UI::Button
+ expand => 1, align => 0, text => "Apply",
+ tooltip => "Apply the audio settings",
+ connect_activate => sub {
+ audio_shutdown ();
+ audio_init ();
+ }
+ );
+
+ $dialog
+}
+
+sub set_stats_window_fontsize {
+ for (values %{$STATWIDS}) {
+ $_->set_fontsize ($::CFG->{stat_fontsize});
+ }
+}
+
+sub set_gauge_window_fontsize {
+ for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
+ $_->set_fontsize ($::CFG->{gauge_fontsize});
+ }
+
+# local $GAUGES->{win}{parent};#d#
+# use PApp::Util; open D, ">:utf8", "d"; print D PApp::Util::dumpval $GAUGES->{win}; close D;
+}
+
+sub make_gauge_window {
+ my $gh = int ($HEIGHT * $CFG->{gauge_size});
+# my $gw = int ($WIDTH * $CFG->{gauge_w_size});
+
+ my $win = new CFClient::UI::Frame (
+ y => $HEIGHT - $gh, x => 0, user_w => $WIDTH, user_h => $gh
+ );
+ $win->add (my $hbox = new CFClient::UI::HBox
+ children => [
+ (new CFClient::UI::HBox expand => 1),
+ ($FLOORBOX = new CFClient::UI::VBox),
+ (my $vbox = new CFClient::UI::VBox),
+ ],
+ );
+
+ $vbox->add (new CFClient::UI::HBox
+ expand => 1,
+ children => [
+ (new CFClient::UI::Empty expand => 1),
+ (my $hb = new CFClient::UI::HBox),
+ ],
+ );
+
+ $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp',
+ tooltip => "Health points - depletes when you get wounded, refills when you heal or idle");
+ $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana',
+ tooltip => "Spell points - deplete when you cast wizard spells, refills when you idle");
+ $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace',
+ tooltip => "Grace points - deplete when you cast priest spells, refills when you pray");
+ $hb->add (my $fg = new CFClient::UI::Gauge type => 'food',
+ tooltip => "Food - depletes with time, faster when you heal or build mana, refills when you eat healthy food");
+
+ $vbox->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
+ tooltip => "Experience points and level - increases when you kill monsters or successfully use skills");
+ $vbox->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
+ tooltip => "Ranged attack - how you attack when you press shift-cursor (spell, skill, weapon etc.)");
+
+ $GAUGES = {
+ exp => $exp, win => $win, range => $rng,
+ food => $fg, mana => $mg, hp => $hg, grace => $gg
+ };
+
+ &set_gauge_window_fontsize;
+
+ $win
+}
+
+sub make_stats_window {
+ my $tgw = new CFClient::UI::FancyFrame (x => $WIDTH * 2/5, y => 0, title => "Stats");
+
+ $tgw->add (my $vb = new CFClient::UI::VBox);
+ $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1);
+ $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1);
+
+ $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
+
+ $hb->add (my $tbl = new CFClient::UI::Table expand => 1);
+
+ my $black = [0, 0, 0];
+
+ $tbl->add (0, 0, $STATWIDS->{st_str} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+ $tbl->add (0, 1, $STATWIDS->{st_dex} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+ $tbl->add (0, 2, $STATWIDS->{st_con} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+ $tbl->add (0, 3, $STATWIDS->{st_int} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+ $tbl->add (0, 4, $STATWIDS->{st_wis} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+ $tbl->add (0, 5, $STATWIDS->{st_pow} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+ $tbl->add (0, 6, $STATWIDS->{st_cha} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
+
+ $tbl->add (1, 0, $STATWIDS->{st_str_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Str");
+ $tbl->add (1, 1, $STATWIDS->{st_dex_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Dex");
+ $tbl->add (1, 2, $STATWIDS->{st_con_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Con");
+ $tbl->add (1, 3, $STATWIDS->{st_int_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Int");
+ $tbl->add (1, 4, $STATWIDS->{st_wis_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Wis");
+ $tbl->add (1, 5, $STATWIDS->{st_pow_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Pow");
+ $tbl->add (1, 6, $STATWIDS->{st_cha_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Cha");
+
+ $tbl->add (2, 0, $STATWIDS->{st_wc} = new CFClient::UI::Label valign => 0, align => +1, template => "-120");
+ $tbl->add (2, 1, $STATWIDS->{st_ac} = new CFClient::UI::Label valign => 0, align => +1, template => "-120");
+ $tbl->add (2, 2, $STATWIDS->{st_dam} = new CFClient::UI::Label valign => 0, align => +1, template => "120");
+ $tbl->add (2, 3, $STATWIDS->{st_arm} = new CFClient::UI::Label valign => 0, align => +1, template => "120");
+ $tbl->add (2, 4, $STATWIDS->{st_spd} = new CFClient::UI::Label valign => 0, align => +1, template => "10.54");
+ $tbl->add (2, 5, $STATWIDS->{st_wspd} = new CFClient::UI::Label valign => 0, align => +1, template => "9");
+
+ $tbl->add (3, 0, $STATWIDS->{st_wc_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Wc");
+ $tbl->add (3, 1, $STATWIDS->{st_ac_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Ac");
+ $tbl->add (3, 2, $STATWIDS->{st_dam_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Dam");
+ $tbl->add (3, 3, $STATWIDS->{st_arm_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Arm");
+ $tbl->add (3, 4, $STATWIDS->{st_spd_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "Sp");
+ $tbl->add (3, 5, $STATWIDS->{st_wspd_lbl} = new CFClient::UI::Label fg => $black, valign => 0, align => -1, text => "WSp");
+
+ $hb->add (my $tbl2 = new CFClient::UI::Table expand => 1);
+
+ my $row = 0;
+ my $col = 0;
+
+ my %resist_names = (
+ slow => "Slow",
+ holyw => "Holy Word",
+ conf => "Confusion",
+ fire => "Fire",
+ depl => "Depletion",
+ magic => "Magic",
+ drain => "Draining",
+ acid => "Acid",
+ pois => "Poison",
+ para => "Paralysation",
+ deat => "Death",
+ phys => "Physical",
+ blind => "Blind",
+ fear => "Fear",
+ tund => "Turn undead",
+ elec => "Electricity",
+ cold => "Cold",
+ ghit => "Ghost hit",
+ );
+ for (qw/slow holyw conf fire depl magic
+ drain acid pois para deat phys
+ blind fear tund elec cold ghit/)
+ {
+ $tbl2->add ($col, $row,
+ $STATWIDS->{"res_$_"} =
+ new CFClient::UI::Label
+ template => "-100%",
+ align => +1,
+ valign => 0,
+ tooltip => $resist_names{$_}
+ );
+ $tbl2->add ($col + 1, $row, new CFClient::UI::Image
+ can_hover => 1,
+ can_events => 1,
+ image => "ui/resist/resist_$_.png",
+ tooltip => $resist_names{$_}
+ );
+
+ $row++;
+ if ($row % 6 == 0) {
+ $col += 2;
+ $row = 0;
+ }
+ }
- for my $num (grep $_, $cell->[0], $cell->[1], $cell->[2]) {
- my $tex = $conn->{face}[$num]{texture} || 0;
-
- glBindTexture GL_TEXTURE_2D, $tex;
+ &set_stats_window_fontsize;
+ update_stats_window ({});
- glColor 1,0.2,0.7;
- glBegin GL_QUADS;
- glTexCoord 0, 0; glVertex $x, $y;
- glTexCoord 0, 1; glVertex $x, $y + 0.9;
- glTexCoord 1, 1; glVertex $x + 0.9, $y + 0.9;
- glTexCoord 1, 0; glVertex $x + 0.9, $y;
- glEnd;
+ $tgw
+}
+
+sub formsep {
+ reverse join ",", grep length, split /(...)/, reverse $_[0] * 1
+}
+
+sub update_stats_window {
+ my ($stats) = @_;
+
+ # i love text protocols!!!
+ my $hp = $stats->{Crossfire::Protocol::CS_STAT_HP} * 1;
+ my $hp_m = $stats->{Crossfire::Protocol::CS_STAT_MAXHP} * 1;
+ my $sp = $stats->{Crossfire::Protocol::CS_STAT_SP} * 1;
+ my $sp_m = $stats->{Crossfire::Protocol::CS_STAT_MAXSP} * 1;
+ my $fo = $stats->{Crossfire::Protocol::CS_STAT_FOOD} * 1;
+ my $fo_m = 999;
+ my $gr = $stats->{Crossfire::Protocol::CS_STAT_GRACE} * 1;
+ my $gr_m = $stats->{Crossfire::Protocol::CS_STAT_MAXGRACE} * 1;
+
+ $GAUGES->{hp} ->set_value ($hp, $hp_m);
+ $GAUGES->{mana} ->set_value ($sp, $sp_m);
+ $GAUGES->{food} ->set_value ($fo, $fo_m);
+ $GAUGES->{grace} ->set_value ($gr, $gr_m);
+ $GAUGES->{exp} ->set_text ("Exp: " . (formsep $stats->{Crossfire::Protocol::CS_STAT_EXP64})
+ . " (lvl " . ($stats->{Crossfire::Protocol::CS_STAT_LEVEL} * 1) . ")");
+ my $rng = $stats->{Crossfire::Protocol::CS_STAT_RANGE};
+ $rng =~ s/^Range: //; # thank you so much dear server
+ $GAUGES->{range} ->set_text ("Rng: " . $rng);
+ my $title = $stats->{Crossfire::Protocol::CS_STAT_TITLE};
+ $title =~ s/^Player: //;
+ $STATWIDS->{title} ->set_text ("Title: " . $title);
+
+ $STATWIDS->{st_str} ->set_text (sprintf "%d", $stats->{5});
+ $STATWIDS->{st_dex} ->set_text (sprintf "%d", $stats->{8});
+ $STATWIDS->{st_con} ->set_text (sprintf "%d", $stats->{9});
+ $STATWIDS->{st_int} ->set_text (sprintf "%d", $stats->{6});
+ $STATWIDS->{st_wis} ->set_text (sprintf "%d", $stats->{7});
+ $STATWIDS->{st_pow} ->set_text (sprintf "%d", $stats->{22});
+ $STATWIDS->{st_cha} ->set_text (sprintf "%d", $stats->{10});
+ $STATWIDS->{st_wc} ->set_text (sprintf "%d", $stats->{13});
+ $STATWIDS->{st_ac} ->set_text (sprintf "%d", $stats->{14});
+ $STATWIDS->{st_dam} ->set_text (sprintf "%d", $stats->{15});
+ $STATWIDS->{st_arm} ->set_text (sprintf "%d", $stats->{16});
+ $STATWIDS->{st_spd} ->set_text (sprintf "%.1f", $stats->{Crossfire::Protocol::CS_STAT_SPEED});
+ $STATWIDS->{st_wspd}->set_text (sprintf "%.1f", $stats->{Crossfire::Protocol::CS_STAT_WEAP_SP});
+
+ my %tbl = (
+ phys => 100,
+ magic => 101,
+ fire => 102,
+ elec => 103,
+ cold => 104,
+ conf => 105,
+ acid => 106,
+ drain => 107,
+ ghit => 108,
+ pois => 109,
+ slow => 110,
+ para => 111,
+ tund => 112,
+ fear => 113,
+ depl => 113,
+ deat => 115,
+ holyw => 116,
+ blind => 117
+ );
+
+ for (keys %tbl) {
+ $STATWIDS->{"res_$_"}->set_text (sprintf "%d%", $stats->{$tbl{$_}});
+ }
+
+}
+
+sub metaserver_dialog {
+ my $dialog = new CFClient::UI::FancyFrame
+ title => "Metaserver",
+ child => (my $vbox = new CFClient::UI::VBox);
+
+ $vbox->add ($dialog->{table} = new CFClient::UI::Table);
+
+ $dialog
+}
+
+sub update_metaserver {
+ my ($HOST) = @_;
+
+ status "fetching metaserver list...";
+
+ my $buf;
+
+ my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0;
+
+ Event->io (fd => $fh, poll => 'r', cb => sub {
+ my $res = sysread $fh, $buf, 8192, length $buf;
+
+ if (!defined $res) {
+ $_[0]->w->cancel;
+ status "metaserver: $!";
+ } elsif ($res == 0) {
+ $_[0]->w->cancel;
+ status "server list retrieved";
+
+ my $table = $METASERVER->{table};
+
+ $table->clear;
+
+ my @col = qw(Use #Users Host Uptime Version Description);
+ $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_])
+ for 0 .. $#col;
+
+ my @align = qw(1 0 1 1 -1);
+
+ my $y = 0;
+ for my $m (sort { $b->[3] <=> $a->[3] } map [split /\|/], split /\015?\012/, $buf) {
+ my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) = @$m;
+
+ for ($desc) {
+ s/
/\n/gi;
+ s/