--- deliantra/Deliantra-Client/DC/Protocol.pm 2006/06/13 14:35:17 1.34 +++ deliantra/Deliantra-Client/DC/Protocol.pm 2006/07/03 22:32:52 1.53 @@ -85,18 +85,92 @@ $fh->flush; } +sub _stat_numdiff { + my ($self, $old, $new) = @_; + my $diff = $new - $old; + $diff > 0 ? "+$diff" : $diff +} + +sub _stat_skillmaskdiff { + my ($self, $old, $new) = @_; + + my $diff = $old ^ $new; + + my @diff = map + { + $diff & $_ + ? (($new & $_ ? "+" : "-") . $self->{spell_paths}{$_}) + : () + } + sort { $a <=> $b } keys %{$self->{spell_paths}}; + + join "", @diff +} + +# all stats that are chacked against changes +my @statchange = ( + [&CS_STAT_STR => \&_stat_numdiff, "Str"], + [&CS_STAT_INT => \&_stat_numdiff, "Int"], + [&CS_STAT_WIS => \&_stat_numdiff, "Wis"], + [&CS_STAT_DEX => \&_stat_numdiff, "Dex"], + [&CS_STAT_CON => \&_stat_numdiff, "Con"], + [&CS_STAT_CHA => \&_stat_numdiff, "Cha"], + [&CS_STAT_POW => \&_stat_numdiff, "Pow"], + [&CS_STAT_WC => \&_stat_numdiff, "Wc"], + [&CS_STAT_AC => \&_stat_numdiff, "Ac"], + [&CS_STAT_DAM => \&_stat_numdiff, "Dam"], + [&CS_STAT_SPEED => \&_stat_numdiff, "Speed"], + [&CS_STAT_WEAP_SP => \&_stat_numdiff, "WSp"], + [&CS_STAT_MAXHP => \&_stat_numdiff, "HP"], + [&CS_STAT_MAXSP => \&_stat_numdiff, "Mana"], + [&CS_STAT_MAXGRACE => \&_stat_numdiff, "Grace"], + [&CS_STAT_WEIGHT_LIM => \&_stat_numdiff, "Weight"], + [&CS_STAT_SPELL_ATTUNE => \&_stat_skillmaskdiff, "attuned"], + [&CS_STAT_SPELL_REPEL => \&_stat_skillmaskdiff, "repelled"], + [&CS_STAT_SPELL_DENY => \&_stat_skillmaskdiff, "denied"], + [&CS_STAT_RES_PHYS => \&_stat_numdiff, "phys"], + [&CS_STAT_RES_MAG => \&_stat_numdiff, "magic"], + [&CS_STAT_RES_FIRE => \&_stat_numdiff, "fire"], + [&CS_STAT_RES_ELEC => \&_stat_numdiff, "electricity"], + [&CS_STAT_RES_COLD => \&_stat_numdiff, "cold"], + [&CS_STAT_RES_CONF => \&_stat_numdiff, "confusion"], + [&CS_STAT_RES_ACID => \&_stat_numdiff, "acid"], + [&CS_STAT_RES_DRAIN => \&_stat_numdiff, "drain"], + [&CS_STAT_RES_GHOSTHIT => \&_stat_numdiff, "ghosthit"], + [&CS_STAT_RES_POISON => \&_stat_numdiff, "poison"], + [&CS_STAT_RES_SLOW => \&_stat_numdiff, "slow"], + [&CS_STAT_RES_PARA => \&_stat_numdiff, "paralyse"], + [&CS_STAT_TURN_UNDEAD => \&_stat_numdiff, "turnundead"], + [&CS_STAT_RES_FEAR => \&_stat_numdiff, "fear"], + [&CS_STAT_RES_DEPLETE => \&_stat_numdiff, "depletion"], + [&CS_STAT_RES_DEATH => \&_stat_numdiff, "death"], + [&CS_STAT_RES_HOLYWORD => \&_stat_numdiff, "godpower"], + [&CS_STAT_RES_BLIND => \&_stat_numdiff, "blind"], +); sub stats_update { my ($self, $stats) = @_; - if (my $exp = $stats->{+CS_STAT_EXP64}) { - my $diff = $exp - $self->{prev_exp}; - $self->{statusbox}->add ("$diff experience gained", group => "experience $diff", fg => [0.5, 1, 0.5, 0.8], timeout => 5) - if exists $self->{prev_exp} && $diff; - $self->{prev_exp} = $exp; + if (my $prev = $self->{prev_stats}) { + if (my $diff = $stats->{+CS_STAT_EXP64} - $prev->{+CS_STAT_EXP64}) { + $self->{statusbox}->add ("$diff experience gained", group => "experience $diff", fg => [0.5, 1, 0.5, 0.8], timeout => 5); + } + + if ( + my @diffs = map { + $stats->{$_->[0]} != $prev->{$_->[0]} + ? $_->[2] . $_->[1]->($self, $prev->{$_->[0]}, $stats->{$_->[0]}) : (); + } + @statchange + ) { + my $msg = "stat change: " . (join " ", @diffs); + $self->{statusbox}->add ($msg, group => $msg, fg => [0.8, 1, 0.2, 1], timeout => 10); + } } + $self->{prev_stats} = { %$stats }; + ::update_stats_window ($stats); } @@ -109,7 +183,7 @@ $self->logprint ("send: ", $command); $self->send_command ($command); - ::status $command; + ::status ($command); } sub start_record { @@ -136,6 +210,12 @@ $self->{map_widget}->update; } +sub magicmap { + my ($self, $w, $h, $x, $y, $data) = @_; + + $self->{map_widget}->set_magicmap ($w, $h, $x, $y, $data); +} + sub flush_map { my ($self) = @_; @@ -156,6 +236,7 @@ delete $self->{neigh_map}; $self->{map}->clear; + delete $self->{map_widget}{magicmap}; } @@ -308,7 +389,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; @@ -409,8 +490,9 @@ $text =~ s/\[b\](.*?)\[\/b\]/\1<\/b>/g; $text =~ s/\[color=(.*?)\](.*?)\[\/color\]/\2<\/span>/g; - $self->{logview}->add_paragraph ($color[$color], - join "\n", map "$time $_", split /\n/, $text); + $self->{logview}->add_paragraph ($color[$color], $_) + for map "$time $_", split /\n/, $text; + $self->{logview}->scroll_to_bottom; $self->{statusbox}->add ($text, group => $text, @@ -434,7 +516,7 @@ $spell->{message} =~ s/\n+$//; $spell->{message} ||= "Server did not provide a description for this spell."; - $::SETUP_SPELLS->add_spell ($spell); + $::SPELL_PAGE->add_spell ($spell); $self->{map_widget}->add_command ("invoke $spell->{name}", CFClient::UI::Label::escape $spell->{message}); $self->{map_widget}->add_command ("cast $spell->{name}", CFClient::UI::Label::escape $spell->{message}); @@ -442,7 +524,8 @@ sub spell_delete { my ($self, $spell) = @_; - $::SETUP_SPELLS->remove_spell ($spell); + + $::SPELL_PAGE->remove_spell ($spell); } sub addme_success { @@ -519,7 +602,7 @@ $::FLOORBOX->clear; my $row; - for (@{ $::CONN->{container}{0} }) { + for (sort { $a->{count} <=> $b->{count} } values %{ $::CONN->{container}{0} }) { if ($row < 6) { local $_->{face_widget}; # hack to force recreation of widget local $_->{desc_widget}; # hack to force recreation of widget @@ -532,7 +615,7 @@ } else { $::FLOORBOX->add (1, $row, new CFClient::UI::Button text => "More...", - on_activate => sub { $::INV_WINDOW->toggle_visibility }, + on_activate => sub { ::toggle_player_page ($::INVENTORY_PAGE); 0 }, ); last; } @@ -557,6 +640,7 @@ $::CONN->send ("apply $tag") # $::CONN->{open_container}") if $tag != 0; #if $CONN->{open_container} != 0; + 0 }, ); } @@ -564,63 +648,46 @@ $::INVR->set_items ($conn->{container}{$tag}); } -sub update_container { - my ($tag) = @_; +sub update_containers { + my ($self) = @_; - $::INVR->set_items ($::CONN->{container}{$::CONN->{open_container}}) - if $tag == $::CONN->{open_container}; + $CFClient::UI::ROOT->on_refresh ("update_containers_$self" => sub { + for my $tag (keys %{ delete $self->{update_container} }) { + if ($tag == 0) { + update_floorbox; + $::INVR->set_items ($self->{container}{0}) + if $tag == $self->{open_container}; + } elsif ($tag == $self->{player}{tag}) { + $::INV->set_items ($self->{container}{$tag}) + } else { + $::INVR->set_items ($self->{container}{$tag}) + if $tag == $self->{open_container}; + } + } + }); } sub container_add { my ($self, $tag, $items) = @_; - #d# print "container_add: container $tag ($self->{player}{tag})\n"; - - if ($tag == 0) { - update_floorbox; - update_container (0); - } elsif ($tag == $self->{player}{tag}) { - $::INV->set_items ($self->{container}{$self->{player}{tag}}) - } else { - update_container ($tag); - } - - # $self-<{player}{tag} => player inv - #use PApp::Util; warn PApp::Util::dumpval $self->{container}{$self->{player}{tag}}; + $self->{update_container}{$tag}++; + $self->update_containers; } sub container_clear { my ($self, $tag) = @_; - #d# print "container_clear: container $tag ($self->{player}{tag})\n"; - - if ($tag == 0) { - update_floorbox; - update_container (0); - } elsif ($tag == $self->{player}{tag}) { - $::INV->set_items ($self->{container}{$tag}) - } else { - update_container ($tag); - } - -# use PApp::Util; warn PApp::Util::dumpval $self->{container}{0}; + $self->{update_container}{$tag}++; + $self->update_containers; } sub item_delete { my ($self, @items) = @_; - for (@items) { - #d# print "item_delete: $_->{tag} from $_->{container} ($self->{player}{tag})\n"; - - if ($_->{container} == 0) { - update_floorbox; - update_container ($_->{tag}); - } elsif ($_->{container} == $self->{player}{tag}) { - $::INV->set_items ($self->{container}{$self->{player}{tag}}) - } else { - update_container ($_->{container}); - } - } + $self->{update_container}{$_->{container}}++ + for @items; + + $self->update_containers; } sub item_update { @@ -628,11 +695,6 @@ #d# print "item_update: $item->{tag} in $item->{container} ($self->{player}{tag}) ($::CONN->{open_container})\n"; - if ($item->{tag} == $self->{player}{tag}) { - $::STATWIDS->{weight}->set_text (sprintf "Weight: %.1fkg", $item->{weight} / 1000); - return; - } - CFClient::Item::update_widgets $item; if ($item->{tag} == $::CONN->{open_container} && not ($item->{flags} & F_OPEN)) { @@ -640,19 +702,21 @@ } elsif ($item->{flags} & F_OPEN) { set_opencont ($::CONN, $item->{tag}, CFClient::Item::desc_string $item); + } else { - if ($item->{container} == 0) { - update_floorbox; - update_container (0); - } elsif ($item->{container} == $self->{player}{tag}) { - $::INV->set_items ($self->{container}{$item->{container}}) - } + $self->{update_container}{$item->{container}}++; + $self->update_containers; +# if ($item->{container} == 0) { +# update_floorbox; +# update_container (0); +# } elsif ($item->{container} == $self->{player}{tag}) { +# $::INV->set_items ($self->{container}{$item->{container}}) +# } } } sub player_update { my ($self, $player) = @_; - $::STATWIDS->{weight}->set_text (sprintf "Weight: %.1fkg", $player->{weight} / 1000); } @@ -758,15 +822,17 @@ $entry->set_text (""); $this->send ($text); + + 0 }, ); $vbox->add ($self->{options} = new CFClient::UI::VBox); - $self->{close_button} = new CFClient::UI::Button + $self->{bye_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 }, + on_activate => sub { $this->destroy; 0 }, ; $self->update_options; @@ -775,7 +841,7 @@ $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->{entry}->grab_focus; $self->{textview}->add_paragraph ([1, 1, 0, 1], "[starting conversation with $self->{title}]\n\n"); @@ -789,13 +855,14 @@ Scalar::Util::weaken $self; $self->{options}->clear; - $self->{options}->add ($self->{close_button}); + $self->{options}->add ($self->{bye_button}); for my $kw (sort keys %{ $self->{kw} }) { $self->{options}->add (new CFClient::UI::Button text => $kw, on_activate => sub { $self->send ($kw); + 0 }, ); } @@ -804,17 +871,37 @@ sub feed { my ($self, $data) = @_; + Scalar::Util::weaken $self; + 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; + $msg = "\n" . 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. + my @link; + $msg =~ s{ + ($match) + }{ + my $kw = $1; + + push @link, new CFClient::UI::Label + markup => "$kw", + can_hover => 1, + can_events => 1, + padding_x => 0, + padding_y => 0, + on_button_up => sub { + $self->send ($kw); + }; + + chr 0xfffc + }giex; - $self->{textview}->add_paragraph ([1, 1, 1, 1], "\n$msg"); + $self->{textview}->add_paragraph ([1, 1, 1, 1], [$msg, @link]); + $self->{textview}->scroll_to_bottom; $self->update_options; } else { $self->destroy; @@ -828,12 +915,13 @@ $self->{conn}->send ("ext npc_dialog_tell $self->{token} $msg"); $self->{textview}->add_paragraph ([1, 1, 0, 1], "\n" . CFClient::UI::Label::escape $msg); + $self->{textview}->scroll_to_bottom; } sub destroy { my ($self) = @_; - #Carp::cluck "debug\n";#d# #todo# enable: destroyx gets called twice because scalar keys {} is 1 + #Carp::cluck "debug\n";#d# #todo# enable: destroy gets called twice because scalar keys {} is 1 delete $self->{conn}{npc_dialog}; $self->{conn}->disconnect_ext ($self->{token});