--- deliantra/Deliantra-Client/DC/Protocol.pm 2006/06/06 03:05:16 1.24
+++ deliantra/Deliantra-Client/DC/Protocol.pm 2006/06/15 13:40:02 1.35
@@ -10,6 +10,8 @@
use base 'Crossfire::Protocol::Base';
+our %open_logs;
+
sub new {
my $class = shift;
@@ -61,6 +63,30 @@
$self
}
+sub logprint {
+ my ($self, @a) = @_;
+ my $filename = "$Crossfire::VARDIR/log.$self->{host}";
+
+ my $fh = $open_logs{$filename};
+ unless ($fh) {
+ # FIXME: handle this more gracefully?
+ open $fh, ">>", $filename
+ or die "Couldn't open logfile: log.$self->{host}: $!";
+
+ $open_logs{$filename} = $fh;
+ }
+
+ my ($sec, $min, $hour, $mday, $mon, $year) = localtime (time);
+
+ my $ts = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
+ $year + 1900, $mon + 1, $mday, $hour, $min, $sec;
+
+ print $fh "$ts ", @a, "\n";
+ $fh->flush;
+}
+
+
+
sub stats_update {
my ($self, $stats) = @_;
@@ -81,6 +107,7 @@
push @{$self->{record}}, $command;
}
+ $self->logprint ("send: ", $command);
$self->send_command ($command);
::status $command;
}
@@ -281,7 +308,7 @@
my $txn = $CFClient::DB_ENV->txn_begin;
my $status = $self->{facemap}->db_get (id => $id);
if ($status == 0 || $status == BerkeleyDB::DB_NOTFOUND) {
- $id = ($id || 16) + 1;
+ $id = ($id || 64) + 1;
if ($self->{facemap}->put (id => $id) == 0
&& $self->{facemap}->put ($hash => $id) == 0) {
$txn->txn_commit;
@@ -371,6 +398,8 @@
[0.74, 0.65, 0.41],
);
+ $self->logprint ("info: ", $text);
+
my $time = sprintf "%02d:%02d:%02d", (localtime time)[2,1,0];
# try to create single paragraphs of multiple lines sent by the server
@@ -400,8 +429,11 @@
sub spell_add {
my ($self, $spell) = @_;
- # TODO
- # create a widget dynamically, using spell face (CF::Protocol downloads them)
+ # try to create single paragraphs of multiple lines sent by the server
+ $spell->{message} =~ s/(?<=\S)\n(?=\w)/ /g;
+ $spell->{message} =~ s/\n+$//;
+ $spell->{message} ||= "Server did not provide a description for this spell.";
+
$::SETUP_SPELLS->add_spell ($spell);
$self->{map_widget}->add_command ("invoke $spell->{name}", CFClient::UI::Label::escape $spell->{message});
@@ -488,7 +520,7 @@
my $row;
for (@{ $::CONN->{container}{0} }) {
- if ($row < 7) {
+ if ($row < 6) {
local $_->{face_widget}; # hack to force recreation of widget
local $_->{desc_widget}; # hack to force recreation of widget
CFClient::Item::update_widgets $_;
@@ -498,7 +530,10 @@
$row++;
} else {
- $::FLOORBOX->add (1, $row, new CFClient::UI::Label text => "More...");
+ $::FLOORBOX->add (1, $row, new CFClient::UI::Button
+ text => "More...",
+ on_activate => sub { $::INV_WINDOW->toggle_visibility },
+ );
last;
}
}
@@ -617,11 +652,193 @@
sub player_update {
my ($self, $player) = @_;
+
$::STATWIDS->{weight}->set_text (sprintf "Weight: %.1fkg", $player->{weight} / 1000);
+}
+
+sub update_server_info {
+ my ($self) = @_;
+
+ my @yesno = ("no", "yes");
+
+ $::SERVER_INFO->set_markup (
+ "server $self->{host}:$self->{port}\n"
+ . "protocol version $self->{version}\n"
+ . "minimap support $yesno[$self->{setup}{mapinfocmd} > 0]\n"
+ . "extended command support $yesno[$self->{setup}{extcmd} > 0]\n"
+ . "cfplus support $yesno[$self->{cfplus_ext} > 0]"
+ . ($self->{cfplus_ext} > 0 ? ", version $self->{cfplus_ext}" : "") ."\n"
+ . "map size $self->{mapw}×$self->{maph}\n"
+ );
+}
+
+sub logged_in {
+ my ($self) = @_;
+
+ $self->send_ext_req (cfplus_support => "1", sub {
+ $self->{cfplus_ext} = $_[0];
+ $self->update_server_info;
+ });
+
+ $self->update_server_info;
- # do it here because it is ignored earlier, and there is no "login" event
$self->send_command ("output-sync $::CFG->{output_sync}");
$self->send_command ("output-count $::CFG->{output_count}");
+ $self->send_command ("pickup $::CFG->{pickup}");
+}
+
+sub lookat {
+ my ($self, $x, $y) = @_;
+
+ if ($self->{cfplus_ext}) {
+ $self->send_ext_req (lookat => "$x $y", sub {
+ my %res = split /\x00/, $_[0];
+
+ if (exists $res{npc_dialog}) {
+ # start npc chat dialog
+ $self->{npc_dialog} = new CFClient::NPCDialog::
+ dx => $x,
+ dy => $y,
+ title => "$res{npc_dialog} (NPC)",
+ conn => $self,
+ ;
+ }
+ });
+ }
+
+ $self->send ("lookat $x $y");
+}
+
+sub destroy {
+ my ($self) = @_;
+
+ $self->{npc_dialog}->destroy
+ if $self->{npc_dialog};
+
+ $self->SUPER::destroy;
+}
+
+package CFClient::NPCDialog;
+
+our @ISA = 'CFClient::UI::FancyFrame';
+
+sub new {
+ my $class = shift;
+
+ my $self = $class->SUPER::new (
+ x => 'center',
+ y => 'center',
+ name => "npc_dialog",
+ force_w => $::WIDTH * 0.7,
+ force_h => $::HEIGHT * 0.7,
+ title => "NPC Dialog",
+ kw => { hi => 0, yes => 0, no => 0 },
+ @_,
+ );
+
+ Scalar::Util::weaken (my $this = $self);
+
+ # better use a pane...
+ $self->add (my $hbox = new CFClient::UI::HBox);
+ $hbox->add ($self->{textview} = new CFClient::UI::TextScroller expand => 1);
+
+ $hbox->add (my $vbox = new CFClient::UI::VBox);
+
+ $vbox->add (new CFClient::UI::Label text => "Message Entry:");
+ $vbox->add ($self->{entry} = new CFClient::UI::Entry
+ tooltip => "Enter a message you want to tell the NPC and press return.\n\n"
+ . "Sometimes you have to tell an NPC something you cannot find out during "
+ . "a normal conversation (such as a password). In those cases you have to use "
+ . "this text entry. You can also enter responses manually instead of using the response "
+ . "buttons below.",
+ on_activate => sub {
+ my ($entry, $text) = @_;
+
+ return unless $text =~ /\S/;
+
+ $entry->set_text ("");
+ $this->send ($text);
+ },
+ );
+
+ $vbox->add ($self->{options} = new CFClient::UI::VBox);
+
+ $self->{close_button} = new CFClient::UI::Button
+ text => "Bye (close)",
+ tooltip => "Use this button to end talking to the NPC. This also closes the dialog window.",
+ on_activate => sub { $this->destroy },
+ ;
+
+ $self->update_options;
+
+ $self->{token} = $self->{conn}->ext_token;
+ $self->{conn}->connect_ext ($self->{token} => sub { $this->feed (@_) });
+ $self->{conn}->send ("ext npc_dialog_begin $self->{token} $self->{dx} $self->{dy}");
+
+ $self->{entry}->focus_in;
+
+ $self->{textview}->add_paragraph ([1, 1, 0, 1], "[starting conversation with $self->{title}]\n\n");
+
+ $self->show;
+ $self
+};
+
+sub update_options {
+ my ($self) = @_;
+
+ Scalar::Util::weaken $self;
+
+ $self->{options}->clear;
+ $self->{options}->add ($self->{close_button});
+
+ for my $kw (sort keys %{ $self->{kw} }) {
+ $self->{options}->add (new CFClient::UI::Button
+ text => $kw,
+ on_activate => sub {
+ $self->send ($kw);
+ },
+ );
+ }
+}
+
+sub feed {
+ my ($self, $data) = @_;
+
+ my ($type, $msg) = split / /, $data, 2;
+
+ if ($type eq "msg") {
+ my ($msg, @kw) = split /\x00/, $msg;
+ $self->{kw}{$_} = 1 for @kw;
+
+ $msg = CFClient::UI::Label::escape $msg;
+ my $match = join "|", map "\\b\Q$_\E\\b", sort { (length $b) <=> (length $a) } keys %{ $self->{kw} };
+ $msg =~ s/($match)/$1<\/span>/gi; # underline when http-ready, huh.
+
+ $self->{textview}->add_paragraph ([1, 1, 1, 1], "\n$msg");
+ $self->update_options;
+ } else {
+ $self->destroy;
+ }
+
+ 1
+}
+
+sub send {
+ my ($self, $msg) = @_;
+
+ $self->{conn}->send ("ext npc_dialog_tell $self->{token} $msg");
+ $self->{textview}->add_paragraph ([1, 1, 0, 1], "\n" . CFClient::UI::Label::escape $msg);
+}
+
+sub destroy {
+ my ($self) = @_;
+
+ #Carp::cluck "debug\n";#d# #todo# enable: destroyx gets called twice because scalar keys {} is 1
+
+ delete $self->{conn}{npc_dialog};
+ $self->{conn}->disconnect_ext ($self->{token});
+
+ $self->SUPER::destroy;
}
1;