ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/game.pl
(Generate patch)

Comparing kgsueme/kgsueme/game.pl (file contents):
Revision 1.26 by pcg, Sun Jun 1 13:09:47 2003 UTC vs.
Revision 1.33 by pcg, Mon Jun 2 14:36:10 2003 UTC

44 44
45 $self->{set} = sub { $self->{time} = $_[0] }; 45 $self->{set} = sub { $self->{time} = $_[0] };
46 46
47 $self->{format} = sub { 47 $self->{format} = sub {
48 if ($_[0] > $low) { 48 if ($_[0] > $low) {
49 format_time $_[0]; 49 format_time $_[0] - $low;
50 } else { 50 } else {
51 sprintf "%s (%d)", (format_time int ($_[0] % $interval) || $interval), $_[0] / $interval; 51 sprintf "%s (%d)", (format_time int ($_[0] % $interval) || $interval), $_[0] / $interval;
52 } 52 }
53 }; 53 };
54 54
57 57
58 $self->{set} = sub { $self->{time} = $_[0]; $self->{moves} = $_[1] }; 58 $self->{set} = sub { $self->{time} = $_[0]; $self->{moves} = $_[1] };
59 59
60 $self->{format} = sub { 60 $self->{format} = sub {
61 if ($_[0] > $low) { 61 if ($_[0] > $low) {
62 format_time $_[0]; 62 format_time $_[0] - $low;
63 } else { 63 } else {
64 sprintf "%s / %d", (format_time int($_[0] % $interval) || $interval), $self->{moves}; 64 sprintf "%s / %d", (format_time int($_[0] % $interval) || $interval), $self->{moves};
65 } 65 }
66 }; 66 };
67 67
81} 81}
82 82
83sub set_time { 83sub set_time {
84 my ($self, $time) = @_; 84 my ($self, $time) = @_;
85 85
86 $self->{start} = $time->[0]; 86 # we ignore requests to re-set the time of a running clock.
87 # this is the easiest way to ensure that commentary etc.
88 # doesn't re-set the clock. yes, this is frickle design,
89 # but I think the protoocl is to blame here, which gives
90 # very little time information. (cgoban2 also has had quite
91 # a lot of small time update problems...)
92 unless ($self->{timeout}) {
87 $self->{set}->($time->[1], $time->[2]); 93 $self->{set}->($time->[0], $time->[1]);
88 94 $self->refresh ($self->{start});
89 $self->refresh ($time->[0]); 95 }
90} 96}
91 97
92sub start { 98sub start {
93 my ($self) = @_; 99 my ($self) = @_;
94 100
95 return if $self->{timeout}; 101 return if $self->{timeout};
96 102
103 # this is correct, since we assume the last message triggered a start
104 $self->{start} = $KGS::Protocol::NOW;
105
97 my $timeout; $timeout = sub { 106 my $timeout; $timeout = sub {
107 # -100 means we run the timer a bit earlier to avoid 10.99 => 10s roundings.
108 # we "could" cheat by precalculating the time, but I feel uneasy about both
109 # ways to cheat.
98 my $next = int ($self->refresh (Time::HiRes::time) * 1000); 110 my $next = int ($self->refresh (Time::HiRes::time) * 1000) - 100;
111 $next += 1000 if $next < 0;
99 $self->{timeout} = add Glib::Timeout $next, $timeout; 112 $self->{timeout} = add Glib::Timeout $next, $timeout;
100 0; 113 0;
101 }; 114 };
102 115
103 $timeout->(); 116 $timeout->();
129 142
130 $vbox->add ($self->{name} = new Gtk2::Label $self->{name}); 143 $vbox->add ($self->{name} = new Gtk2::Label $self->{name});
131 $vbox->add ($self->{info} = new Gtk2::Label ""); 144 $vbox->add ($self->{info} = new Gtk2::Label "");
132 $vbox->add (($self->{clock} = new game::goclock)->widget); 145 $vbox->add (($self->{clock} = new game::goclock)->widget);
133 146
147 $vbox->add ($self->{imagebox} = new Gtk2::VBox);
148
134 $self; 149 $self;
135} 150}
136 151
137sub set_rules { 152sub set_rules {
138 my ($self, $rules) = @_; 153 my ($self, $rules) = @_;
139 154
140 if ($self->{name}->get_text ne $rules->{player}[$self->{colour}]) { 155 if ($self->{name}->get_text ne $rules->{player}[$self->{colour}]) {
141 $self->{name}->set_text ($rules->{player}[$self->{colour}]); 156 $self->{name}->set_text ($rules->{player}[$self->{colour}]);
142 157
158 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children;
159 $self->{imagebox}->add (gtk::image_from_data undef);
160 $self->{imagebox}->show_all;
161
143 # the big picture... 162 # the big picture...
144 appwin::userpic ($rules->{player}[$self->{colour}], sub { 163 appwin::userpic ($rules->{player}[$self->{colour}], sub {
164 if ($_[0]) {
165 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children;
145 $self->{widget}->add (gtk::image_from_data $_[0]) if $_[0]; 166 $self->{imagebox}->add (gtk::image_from_data $_[0]);
146 $self->{widget}->show_all; 167 $self->{imagebox}->show_all;
147 # undef => show sth. funny 168 }
148 }); 169 });
149 } 170 }
150 171
151 $self->{clock}->set_rules (@{$rules->{rules}}{qw(timesys time interval count)}); 172 $self->{clock}->set_rules (@{$rules->{rules}}{qw(timesys time interval count)});
152} 173}
153 174
154sub set_state { 175sub set_state {
155 my ($self, $captures, $timer, $running) = @_; 176 my ($self, $captures, $timer, $running) = @_;
156 177
178 $self->{clock}->stop unless $running;
157 $self->{clock}->set_time ($timer); 179 $self->{clock}->set_time ($timer);
158 $running ? $self->{clock}->start : $self->{clock}->stop; 180 $self->{clock}->start if $running;
181
159 $self->{info}->set_text ("$captures pris."); 182 $self->{info}->set_text ("$captures pris.");
160} 183}
161 184
162package game; 185package game;
163 186
189 }); 212 });
190 213
191 $self->{window}->add($self->{hpane} = new Gtk2::HPaned); 214 $self->{window}->add($self->{hpane} = new Gtk2::HPaned);
192 gtk::state $self->{hpane}, "game::hpane", undef, position => 500; 215 gtk::state $self->{hpane}, "game::hpane", undef, position => 500;
193 216
217 $self->{hpane}->pack1(($self->{left} = new Gtk2::VBox), 1, 1);
218
219 $self->{boardbox} = new Gtk2::VBox;
220
194 $self->{hpane}->pack1((my $vbox = new Gtk2::VBox), 1, 1); 221 $self->{hpane}->pack1((my $vbox = new Gtk2::VBox), 1, 1);
195 222
223 # challenge
224
225 $self->{challenge} = new challenge channel => $self->{channel};
226
227 # board box (aspect/canvas)
228
196 $vbox->pack_start((my $frame = new Gtk2::Frame), 0, 1, 0); 229 $self->{boardbox}->pack_start((my $frame = new Gtk2::Frame), 0, 1, 0);
197 230
198 { 231 {
199 # grrr...
200 $frame->add(my $vbox = new Gtk2::VBox); 232 $frame->add(my $vbox = new Gtk2::VBox);
201 $vbox->add($self->{title} = new Gtk2::Label $title); 233 $vbox->add($self->{title} = new Gtk2::Label $title);
202 234
203 $self->{moveadj} = new Gtk2::Adjustment 1, 0, 1, 0.001, 0.05, 0; 235 $self->{moveadj} = new Gtk2::Adjustment 0, 0, 0, 1, 1, 0;
204 236
205 $vbox->add(my $scale = new Gtk2::HScale $self->{moveadj}); 237 $vbox->add(my $scale = new Gtk2::HScale $self->{moveadj});
206 $scale->set_draw_value (0); 238 $scale->set_draw_value (0);
239 $scale->set_digits (0);
207 240
208 $self->{moveadj}->signal_connect (value_changed => sub { $self->update_board }); 241 $self->{moveadj}->signal_connect (value_changed => sub { $self->update_board });
209 } 242 }
210 243
211 $vbox->pack_start((my $aspect_frame = new Gtk2::AspectFrame "", 0.5, 0.5, 1, 0), 1, 1, 0); 244 $self->{boardbox}->pack_start((my $aspect_frame = new Gtk2::AspectFrame "", 0.5, 0.5, 1, 0), 1, 1, 0);
212 $aspect_frame->set (border_width => 0, shadow_type => 'none', label_xalign => 0.5); 245 $aspect_frame->set (border_width => 0, shadow_type => 'none', label_xalign => 0.5);
213 $self->{board_label} = $aspect_frame->get_label_widget; 246 $self->{board_label} = $aspect_frame->get_label_widget;
214 247
215 $aspect_frame->add($self->{canvas} = new Gtk2::DrawingArea); 248 $aspect_frame->add($self->{canvas} = new Gtk2::DrawingArea);
216 $self->{canvas}->double_buffered (0) if $::config->{conserve_memory}; 249 $self->{canvas}->double_buffered (0) if $::config->{conserve_memory};
233 266
234 $vbox->pack_start((my $hbox = new Gtk2::HBox 1), 0, 1, 0); 267 $vbox->pack_start((my $hbox = new Gtk2::HBox 1), 0, 1, 0);
235 $hbox->add (($self->{userpanel}[WHITE] = new game::userpanel colour => WHITE)->widget); 268 $hbox->add (($self->{userpanel}[WHITE] = new game::userpanel colour => WHITE)->widget);
236 $hbox->add (($self->{userpanel}[BLACK] = new game::userpanel colour => BLACK)->widget); 269 $hbox->add (($self->{userpanel}[BLACK] = new game::userpanel colour => BLACK)->widget);
237 270
238 $vbox->pack_start((my $sw = new Gtk2::ScrolledWindow), 1, 1, 0);
239 $sw->set_policy("never", "always");
240
241 $sw->add(($self->{text} = new gtk::text)->widget); 271 $vbox->pack_start(($self->{text} = new gtk::text)->widget, 1, 1, 0);
242 272
243 $vbox->pack_start(($self->{entry} = new Gtk2::Entry), 0, 1, 0); 273 $vbox->pack_start(($self->{entry} = new Gtk2::Entry), 0, 1, 0);
244 $self->{entry}->signal_connect(activate => sub { 274 $self->{entry}->signal_connect(activate => sub {
245 my $text = $self->{entry}->get_text; 275 my $text = $self->{entry}->get_text;
246 $self->say($text) if $text =~ /\S/; 276 $self->say($text) if $text =~ /\S/;
247 $self->{entry}->set_text(""); 277 $self->{entry}->set_text("");
248 }); 278 });
249 279
280 $self->event_update_game;
250 $self; 281 $self;
251} 282}
252 283
253sub event_update_users { 284sub event_update_users {
254 my ($self, $add, $update, $remove) = @_; 285 my ($self, $add, $update, $remove) = @_;
334 for my $stone ($mark & (MARK_W | MARK_GRAY_W) ? @::white_img : @::black_img) { 365 for my $stone ($mark & (MARK_W | MARK_GRAY_W) ? @::white_img : @::black_img) {
335 my $base = new_pixbuf $size + $shadow, $size + $shadow, 1, 0x00000000; 366 my $base = new_pixbuf $size + $shadow, $size + $shadow, 1, 0x00000000;
336 367
337 # zeroeth the shadow 368 # zeroeth the shadow
338 if ($mark & (MARK_B | MARK_W)) { 369 if ($mark & (MARK_B | MARK_W)) {
370 # the -0.5's are a mystery to me
339 $::shadow_img->composite ( 371 $::shadow_img->composite (
340 $base, $shadow, $shadow, $size, $size, $shadow - 0.5, $shadow - 0.5, 372 $base, $shadow, $shadow, $size, $size, $shadow - 0.5, $shadow - 0.5,
341 $size / $stone->get_width, $size / $stone->get_height, 373 $size / $::shadow_img->get_width, $size / $::shadow_img->get_height,
342 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 192 374 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 192
343 ); 375 );
344 } 376 }
345 377
346 # first the big stones (handicap stones different for effect) 378 # first the big stones (handicap stones different for effect)
362 for ([MARK_SMALL_B, $::black_img[$rand % @::black_img]], 394 for ([MARK_SMALL_B, $::black_img[$rand % @::black_img]],
363 [MARK_SMALL_W, $::white_img[$rand % @::white_img]]) { 395 [MARK_SMALL_W, $::white_img[$rand % @::white_img]]) {
364 my ($mask, $img) = @$_; 396 my ($mask, $img) = @$_;
365 if ($mark & $mask) { 397 if ($mark & $mask) {
366 $img->composite ( 398 $img->composite (
367 $base, (ceil ($size / 4)) x2, (ceil ($size / 2)) x2, (ceil ($size / 4)) x2, 399 $base, (int ($size / 4)) x2, (ceil ($size / 2 + 1)) x2, ($size / 4) x2,
368 $size / $img->get_width / 2, $size / $img->get_height / 2, 400 $size / $img->get_width / 2, $size / $img->get_height / 2,
369 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 224 401 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 224
370 ); 402 );
371 } 403 }
372 } 404 }
378 [MARK_TRIANGLE, $::triangle_img[$dark_bg]], 410 [MARK_TRIANGLE, $::triangle_img[$dark_bg]],
379 [MARK_SQUARE, $::square_img[$dark_bg]]) { 411 [MARK_SQUARE, $::square_img[$dark_bg]]) {
380 my ($mask, $img) = @$_; 412 my ($mask, $img) = @$_;
381 if ($mark & $mask) { 413 if ($mark & $mask) {
382 $img->composite ( 414 $img->composite (
383 $base, 0, 0, $size, $size, -0.5, -0.5, 415 $base, 0, 0, $size, $size, 0, 0,
384 $size / $img->get_width, $size / $img->get_height, 416 $size / $img->get_width, $size / $img->get_height,
385 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 255 417 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 192
386 ); 418 );
387 } 419 }
388 } 420 }
389 421
390 push @$$c, $base; 422 push @$$c, $base;
476 $::board_img->copy_area (0, 0, $s, $s, $pixbuf, 0, 0); 508 $::board_img->copy_area (0, 0, $s, $s, $pixbuf, 0, 0);
477 } else { 509 } else {
478 $pixbuf = scale_pixbuf $::board_img, $s, $s, $::config->{speed} ? INTERP_NEAREST : INTERP_TILES; 510 $pixbuf = scale_pixbuf $::board_img, $s, $s, $::config->{speed} ? INTERP_NEAREST : INTERP_TILES;
479 } 511 }
480 512
481 my $linew = int ($s / 25 / $size); 513 my $linew = int ($s / 40 / $size);
482 514
483 # ornamental border... we have time to waste :/ 515 # ornamental border... we have time to waste :/
484 pixbuf_rect $pixbuf, 0xffcc7700, 0, 0, $s-1, $linew, 255; 516 pixbuf_rect $pixbuf, 0xffcc7700, 0, 0, $s-1, $linew, 255;
485 pixbuf_rect $pixbuf, 0xffcc7700, 0, 0, $linew, $s-1, 255; 517 pixbuf_rect $pixbuf, 0xffcc7700, 0, 0, $linew, $s-1, 255;
486 pixbuf_rect $pixbuf,0xffcc7700, $s-$linew-1, 0, $s-1, $s-1, 255; 518 pixbuf_rect $pixbuf, 0xffcc7700, $s-$linew-1, 0, $s-1, $s-1, 255;
487 pixbuf_rect $pixbuf,0xffcc7700, 0, $s-$linew-1, $s-1, $s-1, 255; 519 pixbuf_rect $pixbuf, 0xffcc7700, 0, $s-$linew-1, $s-1, $s-1, 255;
488 520
489 for my $i (1 .. $size) { 521 for my $i (1 .. $size) {
490 pixbuf_rect $pixbuf, 0x44111100, $k[$i] - $linew, $k[1] - $linew, $k[$i] + $linew, $k[$size] + $linew, 192; 522 pixbuf_rect $pixbuf, 0x44111100, $k[$i] - $linew, $k[1] - $linew, $k[$i] + $linew, $k[$size] + $linew, 192;
491 pixbuf_rect $pixbuf, 0x44111100, $k[1] - $linew, $k[$i] - $linew, $k[$size] + $linew, $k[$i] + $linew, 192; 523 pixbuf_rect $pixbuf, 0x44111100, $k[1] - $linew, $k[$i] - $linew, $k[$size] + $linew, $k[$i] + $linew, 192;
492 524
499 pixbuf_text $pixbuf, 0, $border, $k[$i], $ofs, $size - $i + 1; 531 pixbuf_text $pixbuf, 0, $border, $k[$i], $ofs, $size - $i + 1;
500 pixbuf_text $pixbuf, 0, $s2 + $border, $k[$i], $ofs, $size - $i + 1; 532 pixbuf_text $pixbuf, 0, $s2 + $border, $k[$i], $ofs, $size - $i + 1;
501 533
502 $a++; 534 $a++;
503 $a++ if $a eq "I"; # not correct, instead of AA AB, we should get HH JJ KK... 535 $a++ if $a eq "I"; # not correct, instead of AA AB, we should get HH JJ KK...
536 }
537
538 # hoshi points
539 my $hoshi = sub {
540 my ($x, $y) = @_;
541 my $hs = int ($edge / 4) | 1;
542 $x = $k[$x] - $hs / 2; $y = $k[$y] - $hs / 2;
543
544 # we use the shadow mask... not perfect, but I want to finish this
545 $::shadow_img->composite ($pixbuf,
546 $x, $y, $hs + 1, $hs + 1, $x, $y,
547 $hs / $::shadow_img->get_width, $hs / $::shadow_img->get_height,
548 $::config->{speed} ? INTERP_NEAREST : INTERP_BILINEAR, 255);
549 };
550
551 my $h1 = $size < 10 ? 3 : 4; # corner / edge offset
552 $hoshi->($h1, $h1);
553 $hoshi->($size - $h1 + 1, $h1);
554 $hoshi->($h1, $size - $h1 + 1);
555 $hoshi->($size - $h1 + 1, $size - $h1 + 1);
556
557 if ($size % 2) { # on odd boards, also the remaining 5
558 my $h2 = ($size + 1) / 2;
559 if ($size > 10) {
560 $hoshi->($h1, $h2);
561 $hoshi->($size - $h1 + 1, $h2);
562 $hoshi->($h2, $size - $h1 + 1);
563 $hoshi->($h2, $h1);
564 }
565 # the tengen
566 $hoshi->($h2, $h2);
504 } 567 }
505 568
506 unless ($::config->{conserve_memory} > 1) { 569 unless ($::config->{conserve_memory} > 1) {
507 $self->{background} = $pixbuf; 570 $self->{background} = $pixbuf;
508 $pixbuf = $pixbuf->copy; 571 $pixbuf = $pixbuf->copy;
557 } 620 }
558 pixbuf_text $pixbuf, $white, 621 pixbuf_text $pixbuf, $white,
559 $k[$x], $k[$y], $ofs * 0.7, 622 $k[$x], $k[$y], $ofs * 0.7,
560 $self->{board}{label}[$x-1][$y-1]; 623 $self->{board}{label}[$x-1][$y-1];
561 } 624 }
562
563 # old pixmap&mask-way. that was fast ;(
564 #my ($pm, $bm) = $self->create_stack($gc, $mark, $edge, $x * 17 + $y * 11 );
565
566 #$gc->set_clip_mask ($bm);
567 #$gc->set_clip_origin ($dx, $dy);
568 #$pixmap->draw_pixmap ($gc, $pm, 0, 0, $dx, $dy, $edge, $edge);
569 } 625 }
570 } 626 }
571 } 627 }
572 628
573 $self->{board_shown} = Storable::dclone $self->{board}; 629 $self->{board_shown} = Storable::dclone $self->{board};
593 649
594sub update_board { 650sub update_board {
595 my ($self) = @_; 651 my ($self) = @_;
596 return unless $self->{path}; 652 return unless $self->{path};
597 653
598 $self->{NOW} = Time::HiRes::time; #d# get from msg(!)
599
600 my $move = int (@{$self->{path}} * $self->{moveadj}->get_value); 654 my $move = int $self->{moveadj}->get_value;
601 655
602 my $running = $move == @{$self->{path}}; 656 my $running = $move == @{$self->{path}};
603 657
604 $self->{board_label}->set_text ("Move $move"); 658 $self->{board_label}->set_text ("Move $move");
605 659
612 $self->{userpanel}[BLACK]->set_state ($self->{board}{captures}[BLACK], 666 $self->{userpanel}[BLACK]->set_state ($self->{board}{captures}[BLACK],
613 $self->{board}{timer}[BLACK], 667 $self->{board}{timer}[BLACK],
614 $running && $self->{board}{last} == WHITE); 668 $running && $self->{board}{last} == WHITE);
615 669
616 $self->redraw ($self->repaint_board); 670 $self->redraw ($self->repaint_board);
617
618 $self->{text}->set_text ($self->{board}{comment});
619} 671}
620 672
621sub event_update_tree { 673sub event_update_tree {
622 my ($self) = @_; 674 my ($self) = @_;
623 675
624 $self->{path} = $self->get_path; 676 $self->{path} = $self->get_path;
677
625 $self->{userpanel}[WHITE]->set_rules ($self->{path}[0]); # should be onload only 678 $self->{userpanel}[WHITE]->set_rules ($self->{path}[0]); # should be onload only
626 $self->{userpanel}[BLACK]->set_rules ($self->{path}[0]); # should be onload only 679 $self->{userpanel}[BLACK]->set_rules ($self->{path}[0]); # should be onload only
627 680
628 $self->{moveadj}->value_changed if $self->{moveadj}; 681 if ($self->{moveadj}) {
682 my $upper = $self->{moveadj}->upper;
683 my $pos = $self->{moveadj}->get_value;
684
685 $self->{moveadj}->upper (scalar @{$self->{path}});
686
687 $self->{moveadj}->changed;
688 if ($pos == $upper) {
689 $self->{moveadj}->set_value (scalar @{$self->{path}});
690 } else {
691 $self->{moveadj}->value_changed;
692 }
693 }
694}
695
696sub event_update_comments {
697 my ($self, $node, $comment, $newnode) = @_;
698 $self->SUPER::event_update_comments($node, $comment, $newnode);
699
700 my $text;
701
702 $text .= "\n<header>Move <move>$node->{move}</move>, <header>Node <node>$node->{id}</node></header>"
703 if $newnode;
704
705 for (split /\n/, $comment) {
706 $text .= "\n";
707 if ($_ =~ s/^([0-9a-zA-Z]+ \[[0-9dkp\?\-]+\])://) {
708 $text .= "<user>" . (util::toxml $1) . "</user>:";
709 }
710 $text .= $_;
711 }
712
713 $self->{text}->append_text ($text);
714}
715
716sub event_join {
717 my ($self) = @_;
718 $self->SUPER::event_join;
629} 719}
630 720
631sub event_part { 721sub event_part {
632 my ($self) = @_; 722 my ($self) = @_;
633 $self->SUPER::event_part; 723 $self->SUPER::event_part;
639} 729}
640 730
641sub event_update_game { 731sub event_update_game {
642 my ($self) = @_; 732 my ($self) = @_;
643 $self->SUPER::event_update_game; 733 $self->SUPER::event_update_game;
644 warn "UPDATE GAME";#d# 734 warn "GAME UPDATE ".join (":", %$self);
735 warn "SAVED ".$self->is_saved;
736 warn "SCORED ".$self->is_scored;
737 warn "ADJ ".$self->is_adjourned;
738 warn "VALID ".$self->is_valid;
739 warn "MOVES ".$self->moves;
740 warn "TYPE ".$self->type;
741
742 $self->{left}->remove ($_) for $self->{left}->get_children;
743 if ($self->is_valid) {
744 $self->{left}->add ($self->{boardbox});
745 (delete $self->{challenge})->destroy if $self->{challenge};
746 } else {
747 $self->{left}->add ($self->{challenge}->widget);
748 }
749 $self->{left}->show_all;
645} 750}
646 751
647sub destroy { 752sub destroy {
648 my ($self) = @_; 753 my ($self) = @_;
649 (delete $self->{userpanel}[WHITE])->destroy if $self->{userpanel}[WHITE]; 754 (delete $self->{userpanel}[WHITE])->destroy if $self->{userpanel}[WHITE];

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines