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.81 by pcg, Fri Apr 2 23:22:30 2004 UTC vs.
Revision 1.98 by pcg, Sun May 30 02:22:24 2004 UTC

1use utf8; 1use utf8;
2
3use Scalar::Util ();
2 4
3package game::goclock; 5package game::goclock;
4 6
5# Lo and Behold! I admit it! The rounding stuff etc.. in goclock 7# Lo and Behold! I admit it! The rounding stuff etc.. in goclock
6# is completely borked. 8# is completely borked.
7 9
8use Time::HiRes (); 10use Time::HiRes ();
9 11
10use KGS::Constants; 12use KGS::Constants;
11 13
12use base gtk::widget; 14use Glib::Object::Subclass
15 Gtk2::Label;
13 16
14sub new { 17sub INIT_INSTANCE {
15 my $class = shift; 18 my $self = shift;
16 my $self = $class->SUPER::new(@_);
17 19
18 $self->{widget} = new Gtk2::Label; 20 $self->signal_connect (destroy => sub { $_[0]->stop });
19 21
20 $self->{set} = sub { }; 22 $self->{set} = sub { };
21 $self->{format} = sub { "ERROR" }; 23 $self->{format} = sub { "???" };
22
23 $self;
24} 24}
25 25
26sub configure { 26sub configure {
27 my ($self, $timesys, $main, $interval, $count) = @_; 27 my ($self, $timesys, $main, $interval, $count) = @_;
28 28
75 my $timer = $self->{time} + $self->{start} - $timestamp; 75 my $timer = $self->{time} + $self->{start} - $timestamp;
76 76
77 # we round the timer value slightly... the protocol isn't exact anyways, 77 # we round the timer value slightly... the protocol isn't exact anyways,
78 # and this gives smoother timers ;) 78 # and this gives smoother timers ;)
79 my @format = $self->{format}->(int ($timer + 0.4)); 79 my @format = $self->{format}->(int ($timer + 0.4));
80 $self->{widget}->set_text ($self->{format}->(int ($timer + 0.4))); 80 $self->set_text ($self->{format}->(int ($timer + 0.4)));
81 81
82 $timer - int $timer; 82 $timer - int $timer;
83} 83}
84 84
85sub set_time { 85sub set_time {
118 my ($self) = @_; 118 my ($self) = @_;
119 119
120 remove Glib::Source delete $self->{timeout} if $self->{timeout}; 120 remove Glib::Source delete $self->{timeout} if $self->{timeout};
121} 121}
122 122
123sub destroy { 123package game::userpanel;
124
125use Glib::Object::Subclass
126 Gtk2::HBox,
127 properties => [
128 Glib::ParamSpec->IV ("colour", "colour", "User Colour", 0, 1, 0, [qw(construct-only writable)]),
129 ];
130
131sub INIT_INSTANCE {
124 my ($self) = @_; 132 my ($self) = @_;
125 $self->stop;
126 $self->SUPER::destroy;
127}
128 133
129package game::userpanel;
130
131use base gtk::widget;
132
133sub new {
134 my $class = shift;
135 my $self = $class->SUPER::new(@_);
136
137 $self->{widget} = new Gtk2::HBox;
138
139 $self->{widget}->add (my $vbox = new Gtk2::VBox); 134 $self->add (my $vbox = new Gtk2::VBox);
140 135
141 $vbox->add ($self->{name} = new Gtk2::Label $self->{name}); 136 $vbox->add ($self->{name} = new Gtk2::Label $self->{name});
142 $vbox->add ($self->{info} = new Gtk2::Label ""); 137 $vbox->add ($self->{info} = new Gtk2::Label "");
143 $vbox->add (($self->{clock} = new game::goclock)->widget); 138 $vbox->add ($self->{clock} = new game::goclock); Scalar::Util::weaken $self->{clock};
144 139
145 $vbox->add ($self->{imagebox} = new Gtk2::VBox); 140 $vbox->add ($self->{imagebox} = new Gtk2::VBox);
146 141
147 $self; 142 $self;
148} 143}
184 $self->{info}->set_text ("$captures pris."); 179 $self->{info}->set_text ("$captures pris.");
185} 180}
186 181
187package game; 182package game;
188 183
184use Scalar::Util qw(weaken);
185
189use KGS::Constants; 186use KGS::Constants;
190use KGS::Game::Board; 187use KGS::Game::Board;
191 188
192use Gtk2::GoBoard; 189use Gtk2::GoBoard;
190use Gtk2::GoBoard::Constants;
191
192use Glib::Object::Subclass
193 Gtk2::Window;
193 194
194use base KGS::Listener::Game; 195use base KGS::Listener::Game;
195use base KGS::Game; 196use base KGS::Game;
196 197
197use base gtk::widget;
198
199use POSIX qw(ceil); 198use POSIX qw(ceil);
200 199
201sub new { 200sub new {
202 my $self = shift; 201 my ($self, %arg) = @_;
203 $self = $self->SUPER::new(@_); 202 $self = $self->Glib::Object::new;
203 $self->{$_} = delete $arg{$_} for keys %arg;
204 204
205 $self->listen($self->{conn});
206
207 $self->{window} = new Gtk2::Window 'toplevel';
208 gtk::state $self->{window}, "game::window", undef, window_size => [600, 500]; 205 gtk::state $self, "game::window", undef, window_size => [600, 500];
209 206
210 $self->{window}->signal_connect(delete_event => sub { 207 $self->signal_connect (destroy => sub {
211 $self->part; 208 $self->unlisten;
212 $self->destroy; 209 delete $self->{app}{game}{$self->{channel}};
213 1; 210 %{$_[0]} = ();
214 }); 211 });#d#
215 212
216 $self->{window}->add($self->{hpane} = new Gtk2::HPaned); 213 $self->add (my $hpane = new Gtk2::HPaned);
217 gtk::state $self->{hpane}, "game::hpane", undef, position => 500; 214 gtk::state $hpane, "game::hpane", undef, position => 500;
218 215
219 # LEFT PANE 216 # LEFT PANE
220 217
221 $self->{hpane}->pack1(($self->{left} = new Gtk2::VBox), 1, 0); 218 $hpane->pack1 (($self->{left} = new Gtk2::VBox), 1, 0);
222 219
223 $self->{boardbox} = new Gtk2::VBox; 220 $self->{boardbox} = new Gtk2::VBox;
224 221
225 $self->{hpane}->pack1((my $vbox = new Gtk2::VBox), 1, 1); 222 $hpane->pack1((my $vbox = new Gtk2::VBox), 1, 1);
226 223
227 # challenge
228
229 $self->{challenge} = new challenge channel => $self->{channel};
230
231 # board box (aspect/canvas) 224 # board box (aspect/canvas)
232 225
233 $self->{boardbox}->pack_start((my $frame = new Gtk2::Frame), 0, 1, 0); 226 #$self->{boardbox}->pack_start((my $frame = new Gtk2::Frame), 0, 1, 0);
227
228 # RIGHT PANE
229
230 $hpane->pack2 ((my $vbox = new Gtk2::VBox), 1, 1);
231 $hpane->set (position_set => 1);
232
233 $vbox->pack_start ((my $frame = new Gtk2::Frame), 0, 1, 0);
234 234
235 { 235 {
236 $frame->add (my $vbox = new Gtk2::VBox); 236 $frame->add (my $vbox = new Gtk2::VBox);
237 $vbox->add ($self->{title} = new Gtk2::Label $title); 237 $vbox->add ($self->{title} = new Gtk2::Label $title);
238 238
247 $scale->set_digits (0); 247 $scale->set_digits (0);
248 248
249 $self->{moveadj}->signal_connect (value_changed => sub { $self->update_board }); 249 $self->{moveadj}->signal_connect (value_changed => sub { $self->update_board });
250 } 250 }
251 251
252 $self->{boardbox}->add ($self->{board} = new Gtk2::GoBoard size => $self->{size});
253
254 # RIGHT PANE
255
256 $self->{hpane}->pack2(($self->{vpane} = new Gtk2::VPaned), 1, 1);
257 $self->{hpane}->set(position_set => 1);
258 gtk::state $self->{vpane}, "game::vpane", $self->{name}, position => 80;
259
260 $self->{vpane}->add(my $sw = new Gtk2::ScrolledWindow);
261 $sw->set_policy("automatic", "always");
262
263 $sw->add(($self->{userlist} = new userlist)->widget);
264
265 $self->{vpane}->add(my $vbox = new Gtk2::VBox);
266
267 $vbox->pack_start((my $hbox = new Gtk2::HBox 1), 0, 1, 0); 252 $vbox->pack_start ((my $hbox = new Gtk2::HBox 1), 0, 1, 0);
253
268 $hbox->add (($self->{userpanel}[COLOUR_WHITE] = new game::userpanel colour => COLOUR_WHITE)->widget); 254 $hbox->add ($self->{userpanel}[$_] = new game::userpanel colour => $_)
269 $hbox->add (($self->{userpanel}[COLOUR_BLACK] = new game::userpanel colour => COLOUR_BLACK)->widget); 255 for COLOUR_WHITE, COLOUR_BLACK;
270 256
271 $vbox->pack_start(($self->{chat} = new chat), 1, 1, 0); 257 $vbox->pack_start (($self->{chat} = new superchat), 1, 1, 0);
272 258
273 $self->{chat}->signal_connect(command => sub { 259 $self->set_channel ($self->{channel});
274 my ($chat, $cmd, $arg) = @_;
275 if ($cmd eq "rsave") {
276 Storable::nstore { tree => $self->{tree}, curnode => $self->{curnode}, move => $self->{move} }, $arg;#d#
277 } else {
278 $self->{app}->do_command ($chat, $cmd, $arg, userlist => $self->{userlist}, game => $self);
279 }
280 });
281 260
282 $self->event_update_game; 261 $self->show_all;
262
283 $self; 263 $self;
264}
265
266sub set_channel {
267 my ($self, $channel) = @_;
268
269 $self->{channel} = $channel;
270
271 if ($self->{channel} > 0) {
272 $self->listen ($self->{conn});
273
274 $self->{rules_inlay} = $self->{chat}->new_switchable_inlay ("Game Setup:", sub { $self->draw_setup (@_) }, 1);
275 $self->{users_inlay} = $self->{chat}->new_switchable_inlay ("Users:", sub { $self->draw_users (@_) }, 1);
276
277 $self->signal_connect (delete_event => sub { $self->part; 1 });
278 $self->{chat}->signal_connect (command => sub {
279 my ($chat, $cmd, $arg) = @_;
280 if ($cmd eq "rsave") {
281 Storable::nstore { tree => $self->{tree}, curnode => $self->{curnode}, move => $self->{move} }, $arg;#d#
282 } else {
283 $self->{app}->do_command ($chat, $cmd, $arg, userlist => $self->{userlist}, game => $self);
284 }
285 });
286 }
284} 287}
285 288
286sub event_update_users { 289sub event_update_users {
287 my ($self, $add, $update, $remove) = @_; 290 my ($self, $add, $update, $remove) = @_;
288 291
289 return unless $self->{userlist};
290
291 $self->{userlist}->update ($add, $update, $remove); 292# $self->{userlist}->update ($add, $update, $remove);
293
294 $self->{challenge}{$_->{name}} && (delete $self->{challenge}{$_->{name}})->{inlay}->destroy
295 for @$remove;
296
297 $self->{users_inlay}->refresh;
292 298
293 my %important; 299 my %important;
300 $important{$self->{black}{name}}++;
301 $important{$self->{white}{name}}++;
294 $important{$self->{user1}{name}}++; 302 $important{$self->{owner}{name}}++;
295 $important{$self->{user2}{name}}++;
296 $important{$self->{user3}{name}}++;
297 303
298 if (my @users = grep $important{$_->{name}}, @$add) { 304 if (my @users = grep $important{$_->{name}}, @$add) {
299 $self->{chat}->append_text ("\n<header>Joins:</header>"); 305 $self->{chat}->append_text ("\n<header>Joins:</header>");
300 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users; 306 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users;
301 } 307 }
302 if (my @users = grep $important{$_->{name}}, @$remove) { 308 if (my @users = grep $important{$_->{name}}, @$remove) {
303 $self->{chat}->append_text ("\n<header>Parts:</header>"); 309 $self->{chat}->append_text ("\n<header>Parts:</header>");
304 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users; 310 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users;
305 } 311 }
306
307} 312}
308 313
309sub join { 314sub join {
310 my ($self) = @_; 315 my ($self) = @_;
311 return if $self->{joined}; 316 return if $self->{joined};
312 317
313 $self->SUPER::join; 318 $self->SUPER::join;
314
315 $self->{window}->show_all;
316} 319}
317 320
318sub part { 321sub update_cursor {
319 my ($self) = @_; 322 my ($self) = @_;
320 323
321 $self->SUPER::part; 324 my $move = int $self->{moveadj}->get_value;
322 $self->destroy; 325 my $cb;
326
327 my $running = $move == @{$self->{path}}
328 && $self->is_active;
329
330 warn "plaiyng $self->{playing} running $running teach $self->{teacher}\n";
331 warn !($self->{playing} && $running);
332 warn $self->{teacher} ne $self->{app}{conn};
333 if (!($self->{playing} && $running) && $self->{teacher} ne $self->{app}{conn}) {
334 warn "A\n";
335 $self->{board}->set (cursor_mask => 0, cursor_value => 0);
336 } elsif ($self->{black}{name} eq $self->{conn}{name}) {
337 warn "B\n";
338 $self->{board}->set (cursor_mask => MARK_B | MARK_W, cursor_value => MARK_B | MARK_GRAYED);
339 $cb = sub {
340 $self->send (upd_tree =>
341 channel => $self->{channel},
342 tree => $self->gen_move_tree (COLOUR_BLACK, $_[0], $_[1]),
343 );
344 };
345 } elsif ($self->{white}{name} eq $self->{conn}{name}) {
346 warn "C\n";
347 $self->{board}->set (cursor_mask => MARK_B | MARK_W, cursor_value => MARK_W | MARK_GRAYED);
348 $cb = sub {
349 $self->send (upd_tree =>
350 channel => $self->{channel},
351 tree => $self->gen_move_tree (COLOUR_WHITE, $_[0], $_[1]),
352 );
353 };
354 }
355
356 $self->{board_click} = $cb;
323} 357}
324 358
325sub update_board { 359sub update_board {
326 my ($self) = @_; 360 my ($self) = @_;
327 return unless $self->{path}; 361 return unless $self->{path};
328 362
329 my $move = int $self->{moveadj}->get_value; 363 my $move = int $self->{moveadj}->get_value;
330 364
331 my $running = $move == @{$self->{path}}; 365 my $running = $move == @{$self->{path}}
366 && !$self->{teacher}
367 && $self->is_active;
332 368
333 $self->{board_label}->set_text ("Move " . ($move - 1)); 369 $self->{board_label}->set_text ("Move " . ($move - 1));
334 370
335 $self->{cur_board} = new KGS::Game::Board $self->{size}; 371 $self->{cur_board} = new KGS::Game::Board $self->{size};
336 $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $move - 1]]); 372 $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $move - 1]]);
343 ? $self->{lastmove_time} : 0 379 ? $self->{lastmove_time} : 0
344 ); 380 );
345 } 381 }
346 382
347 $self->{board}->set_board ($self->{cur_board}); 383 $self->{board}->set_board ($self->{cur_board});
384
385 $self->update_cursor;
348} 386}
349 387
350sub event_update_tree { 388sub event_update_tree {
351 my ($self) = @_; 389 my ($self) = @_;
352 390
404 $self->{chat}->append_text ($text); 442 $self->{chat}->append_text ($text);
405} 443}
406 444
407sub event_join { 445sub event_join {
408 my ($self) = @_; 446 my ($self) = @_;
447
409 $self->SUPER::event_join; 448 $self->SUPER::event_join (@_);
449 $self->init_tree;
450 $self->event_update_game;
410} 451}
411 452
412sub event_part { 453sub event_part {
413 my ($self) = @_; 454 my ($self) = @_;
455
414 $self->SUPER::event_part; 456 $self->SUPER::event_part;
415 $self->destroy; 457 $self->destroy;
416} 458}
417 459
418sub event_move { 460sub event_move {
419 my ($self, $pass) = @_; 461 my ($self, $pass) = @_;
420 sound::play 1, $pass ? "pass" : "move"; 462 sound::play 1, $pass ? "pass" : "move";
463
464 if ($self->{undo_inlay}) {
465 (delete $self->{undo_inlay})->clear;
466 }
421} 467}
422 468
423sub event_update_game { 469sub event_update_game {
424 my ($self) = @_; 470 my ($self) = @_;
471
425 $self->SUPER::event_update_game; 472 $self->SUPER::event_update_game;
426 473
427 return unless $self->{window}; 474 return unless $self->{joined};
428 475
476 $self->{playing} = $self->{teacher} eq $self->{conn}{name}
477 || $self->is_playing ($self->{conn}{name});
478
429 my $title = defined $self->{channel} 479 my $title = defined $self->{channel}
430 ? $self->owner->as_string . " " . $self->opponent_string 480 ? $self->owner->as_string . " " . $self->opponent_string
431 : "Game Window"; 481 : "Game Window";
432 $self->{window}->set_title("KGS Game $title"); 482 $self->set_title("KGS Game $title");
433 $self->{title}->set_text ($title); 483 $self->{title}->set_text ($title);
434 484
435 $self->{user}[COLOUR_BLACK] = $self->{user1}; 485 $self->{user}[COLOUR_BLACK] = $self->{black};
436 $self->{user}[COLOUR_WHITE] = $self->{user2}; 486 $self->{user}[COLOUR_WHITE] = $self->{white};
437 487
438 # show board 488 # show board
489 if ($self->is_inprogress) {
490 if (!$self->{boardbox}->parent) {
491 $self->{boardbox}->add ($self->{board} = new Gtk2::GoBoard size => $self->{size});
492 $self->{left}->add ($self->{boardbox});
493 $self->{board}->signal_connect (button_release => sub {
494 warn "XXX @_\n";#d#
495 if ($_[1] == 1) {
496 $self->{board_click}->($_[2], $_[3]) if $self->{board_click};
497 }
498 });
499 }
500 if (my $ch = delete $self->{challenge}) {
501 $_->{inlay}->destroy for values %$ch;
502 }
503 $self->update_cursor;
504 }
505
506 $self->{left}->show_all;
507
508 $self->{rules_inlay}->refresh;
439 509
510}
511
512sub draw_setup {
513 my ($self, $inlay) = @_;
514
515 return unless $self->{joined};
516
517 my $rules = $self->{rules};
518
519 my $text = "";
520
521 $text .= "\nTeacher: <user>" . (util::toxml $self->{teacher}) . "</user>"
522 if $self->{teacher};
523
524 $text .= "\nOwner: <user>" . (util::toxml $self->{owner}->as_string) . "</user>"
525 if $self->{owner}->is_valid;
526
440 if ($self->is_inprogress) { 527 if ($self->is_inprogress) {
441 $self->{left}->remove ($self->{challenge}->widget) if $self->{challenge} && $self->{boardbox}->parent;
442 $self->{left}->add ($self->{boardbox}) unless $self->{boardbox}->parent;
443 } else {
444 $self->{left}->remove ($self->{boardbox}) if $self->{boardbox}->parent;
445 $self->{left}->add ($self->{challenge}->widget) unless $self->{challenge}->widget->parent;
446 }
447 $self->{left}->show_all;
448
449 # view text
450
451 eval { #d#
452 my @ga;
453 $ga[0] = "\nType: " . (util::toxml $gametype{$self->type})
454 . " (" . (util::toxml $gameopt{$self->option}) . ")";
455 $ga[1] = "\nFlags:";
456 $ga[1] .= " started" if $self->is_inprogress;
457 $ga[1] .= " adjourned" if $self->is_adjourned;
458 $ga[1] .= " scored" if $self->is_scored;
459 $ga[1] .= " saved" if $self->is_saved;
460
461 $ga[2] = "\nOwner: <user>" . (util::toxml $self->{user3}->as_string) . "</user>"
462 if $self->{user3}->is_inprogress;
463
464 $ga[3] = "\nPlayers: <user>" . (util::toxml $self->{user2}->as_string) . "</user>" 528 $text .= "\nPlayers: <user>" . (util::toxml $self->{white}->as_string) . "</user>"
465 . " vs. <user>" . (util::toxml $self->{user1}->as_string) . "</user>" 529 . " vs. <user>" . (util::toxml $self->{black}->as_string) . "</user>";
466 if $self->is_inprogress;
467
468 if ($self->is_inprogress) {
469 $ga[4] = "\nHandicap: " . $self->{handicap};
470 $ga[5] = "\nKomi: " . $self->{komi};
471 $ga[6] = "\nSize: " . $self->size_string;
472 }
473
474 if ($self->is_scored) {
475 $ga[7] = "\nResult: " . $self->score_string;
476 }
477
478 $text = "\n<infoblock><header>Game Update</header>";
479 for (0..7) {
480 if ($self->{gatext}[$_] ne $ga[$_]) {
481 $text .= $ga[$_];
482 }
483 }
484 $text .= "</infoblock>";
485
486 $self->{gatext} = \@ga;
487 }; 530 }
488 531 $text .= "\nType: " . util::toxml $gametype{$self->type};
489 $self->{chat}->append_text ($text);
490}
491
492sub event_update_rules {
493 my ($self, $rules) = @_;
494
495 $self->{userpanel}[$_]->configure ($self->{app}, $self->{user}[$_], $rules)
496 for COLOUR_BLACK, COLOUR_WHITE;
497
498 sound::play 3, "gamestart";
499
500 my $text = "\n<header>Game Rules</header>";
501 532
502 $text .= "\nRuleset: " . $ruleset{$rules->{ruleset}}; 533 $text .= "\nRuleset: " . $ruleset{$rules->{ruleset}};
503 534
504 $text .= "\nTime: "; 535 $text .= "\nTime: ";
505 536
514 } elsif ($rules->{timesys} == TIMESYS_CANADIAN) { 545 } elsif ($rules->{timesys} == TIMESYS_CANADIAN) {
515 $text .= util::format_time $rules->{time}; 546 $text .= util::format_time $rules->{time};
516 $text .= sprintf " + %s/%d CAN", util::format_time $rules->{interval}, $rules->{count}; 547 $text .= sprintf " + %s/%d CAN", util::format_time $rules->{interval}, $rules->{count};
517 } 548 }
518 549
550 $text .= "\nFlags:";
551 $text .= " private" if $self->is_private;
552 $text .= " started" if $self->is_inprogress;
553 $text .= " adjourned" if $self->is_adjourned;
554 $text .= " scored" if $self->is_scored;
555 $text .= " saved" if $self->is_saved;
556
557 if ($self->is_inprogress) {
558 $text .= "\nHandicap: " . $self->{handicap};
559 $text .= "\nKomi: " . $self->{komi};
560 $text .= "\nSize: " . $self->size_string;
561 }
562
563 if ($self->is_scored) {
564 $text .= "\nResult: " . $self->score_string;
565 }
566
519 $self->{chat}->append_text ("<infoblock>$text</infoblock>"); 567 $inlay->append_text ("<infoblock>$text</infoblock>");
568
569}
570
571sub event_update_rules {
572 my ($self, $rules) = @_;
573
574 $self->{rules} = $rules;
575
576 if ($self->{user}) {
577 $self->{userpanel}[$_]->configure ($self->{app}, $self->{user}[$_], $rules)
578 for COLOUR_BLACK, COLOUR_WHITE;
579 }
580
581 sound::play 3, "gamestart";
582
583 $self->{rules_inlay}->refresh;
520} 584}
521 585
522sub inject_resign_game { 586sub inject_resign_game {
523 my ($self, $msg) = @_; 587 my ($self, $msg) = @_;
524 588
538 . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string) 602 . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string)
539 . "</infoblock>" 603 . "</infoblock>"
540 ); 604 );
541} 605}
542 606
607sub inject_req_undo {
608 my ($self, $msg) = @_;
609
610 my $inlay = $self->{undo_inlay} ||= $self->{chat}->new_inlay;
611 return if $inlay->{ignore};
612
613 $inlay->{count}++;
614 $inlay->clear;
615 $inlay->append_text ("\n<undo>Undo requested ($inlay->{count} times)</undo>\n");
616 $inlay->append_button ("Grant", sub {
617 $inlay->clear;
618 $self->send (grant_undo => channel => $self->{channel});
619 });
620 $inlay->append_button ("Ignore", sub {
621 $inlay->clear;
622 $inlay->{ignore} = 1;
623 # but leave inlay, so further undo requests get counted
624 });
625
626}
627
628sub inject_new_game {
629 my ($self, $msg) = @_;
630
631 $self->{chat}->append_text ("\n<header>ACK from server ($msg->{cid} == $self->{cid})</header>");
632 delete $self->{cid};
633}
634
635sub draw_challenge {
636 my ($self, $id) = @_;
637
638 my $info = $self->{challenge}{$id};
639 my $inlay = $info->{inlay};
640 my $rules = $info->{rules};
641 warn "drawchal $id\n";#d#
642# use PApp::Util; warn PApp::Util::dumpval $challenge;#d#
643
644 my $as_black = $info->{black}{name} eq $self->{conn}{name} ? 1 : 0;;
645 my $opponent = $as_black ? $info->{white} : $info->{black};
646
647 my ($size, $time, $interval, $count);
648
649 if (!$self->{channel}) {
650 $inlay->append_text ("\nNotes: ");
651 $inlay->append_entry (\$info->{notes}, 20, "");
652 $inlay->append_text ("\nGlobal Offer: ");
653 $inlay->append_optionmenu (\$info->{flags},
654 0 => "No",
655 2 => "Yes",
656 );
657 } else {
658 $inlay->append_text ("\nNotes: " . util::toxml $info->{notes});
659 }
660
661 $inlay->append_text ("\nType: ");
662 $inlay->append_optionmenu (
663 \$info->{gametype},
664 GAMETYPE_DEMONSTRATION , "Demonstration",
665 GAMETYPE_DEMONSTRATION | GAMETYPE_PRIVATE, "Demonstration (P)",
666 GAMETYPE_TEACHING , "Teaching",
667 GAMETYPE_TEACHING | GAMETYPE_PRIVATE, "Teaching (P)",
668 GAMETYPE_SIMUL , "Simul (not yet!)",
669 GAMETYPE_FREE , "Free",
670 GAMETYPE_RATED , "Rated",
671 sub {
672 $size->set_history (2) if $_[0] eq GAMETYPE_RATED;
673 },
674 );
675
676 if ($self->{channel}) {
677 $inlay->append_text ("\nMy Colour: ");
678 $inlay->append_optionmenu (
679 \$as_black,
680 0 => "White",
681 1 => "Black",
682 sub {
683 if ($info->{$_[0] ? "black" : "white"}{name} ne $self->{conn}{name}) {
684 ($info->{black}, $info->{white}) = ($info->{white}, $info->{black});
685 }
686 }
687 );
688 }
689
690 $inlay->append_text ("\nRuleset: ");
691 $inlay->append_optionmenu (
692 \$info->{rules}{ruleset},
693 RULESET_JAPANESE , "Japanese",
694 RULESET_CHINESE , "Chinese",
695 RULESET_AGA , "AGA",
696 RULESET_NEW_ZEALAND, "New Zealand",
697 );
698
699 $inlay->append_text ("\nSize: ");
700 $size = $inlay->append_optionmenu (
701 \$info->{rules}{size}, 9 => 9, 13 => 13, 19 => 19, map +($_, $_), 2..38
702 );
703
704 if ($self->{channel}) {
705 $inlay->append_text ("\nHandicap: ");
706 $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9);
707
708 $inlay->append_text ("\nKomi: ");
709 $inlay->append_entry (\$info->{rules}{komi}, 5);
710 }
711
712 $inlay->append_text ("\nTimesys: ");
713 $inlay->append_optionmenu (
714 \$info->{rules}{timesys},
715 &TIMESYS_NONE => "None",
716 &TIMESYS_ABSOLUTE => "Absolute",
717 &TIMESYS_BYO_YOMI => "Byo Yomi",
718 &TIMESYS_CANADIAN => "Canadian",
719 sub {
720 my ($new) = @_;
721
722 if ($new eq TIMESYS_NONE) {
723 $time->hide;
724 $interval->hide;
725 $count->hide;
726 } else {
727 $time->show;
728 $time->set_text ($self->{app}{defaults}{time});
729 if ($new eq TIMESYS_ABSOLUTE) {
730 $interval->hide;
731 $count->hide;
732 } else {
733 $interval->show;
734 $count->show;
735 if ($new eq TIMESYS_BYO_YOMI) {
736 $interval->set_text ($self->{app}{defaults}{byo_time});
737 $count->set_text ($self->{app}{defaults}{byo_period});
738 } elsif ($new eq TIMESYS_CANADIAN) {
739 $interval->set_text ($self->{app}{defaults}{can_time});
740 $count->set_text ($self->{app}{defaults}{can_period});
741 }
742 }
743 }
744 }
745 );
746
747 $inlay->append_text ("\nMain Time: ");
748 $time = $inlay->append_entry (\$info->{rules}{time}, 5);
749 $inlay->append_text ("\nInterval: ");
750 $interval = $inlay->append_entry (\$info->{rules}{interval}, 3);
751 $inlay->append_text ("\nPeriods/Stones: ");
752 $count = $inlay->append_entry (\$info->{rules}{count}, 2);
753
754 $inlay->append_text ("\n");
755
756 if (!$self->{channel}) {
757 $inlay->append_button ("Create Challenge", sub {
758 $inlay->clear;
759 $self->{cid} = $self->{conn}->alloc_clientid;
760 $self->send (new_game =>
761 channel => delete $self->{roomid},
762 gametype => $info->{gametype},
763 cid => $self->{cid},
764 flags => $info->{flags},
765 rules => $info->{rules},
766 notes => $info->{notes},
767 );
768 });
769 } else {
770 $inlay->append_button ("OK", sub {
771 $inlay->clear;
772 $self->send (challenge =>
773 channel => $self->{channel},
774 black => $info->{black},
775 white => $info->{white},
776 gametype => $info->{gametype},
777 cid => $info->{cid},
778 rules => $info->{rules},
779 );
780 });
781 if (exists $self->{challenge}{""}) {
782 $inlay->append_button ("Reject", sub {
783 $inlay->clear;
784 $self->send (reject_challenge =>
785 channel => $self->{channel},
786 name => $opponent->{name},
787 gametype => $info->{gametype},
788 cid => $info->{cid},
789 rules => $info->{rules},
790 );
791 });
792 }
793 }
794}
795
796sub draw_users {
797 my ($self, $inlay) = @_;
798
799 for (sort keys %{$self->{users}}) {
800 $inlay->append_text (" <user>" . $self->{users}{$_}->as_string . "</user>");
801 }
802}
803
543sub event_challenge { 804sub event_challenge {
544 my ($self, $challenge) = @_;
545
546 use KGS::Listener::Debug;
547 $self->{chat}->append_text ("\n".KGS::Listener::Debug::dumpval($challenge));
548}
549
550sub destroy {
551 my ($self) = @_; 805 my ($self, $info) = @_;
552 806
553 delete $self->{app}{gamelist}{game}{$self->{channel}}; 807 my $as_black = $info->{black}->{name} eq $self->{conn}{name};
554 $self->{userpanel}[$_] && (delete $self->{userpanel}[$_])->destroy 808 my $opponent = $as_black ? $info->{white} : $info->{black};
555 for COLOUR_BLACK, COLOUR_WHITE; 809
556 $self->SUPER::destroy; 810 my $id = $opponent->{name};
811
812 warn "challenge from $opponent->{name}\n";#d#
813
814 $self->{challenge}{$id} = $info;
815 $self->{challenge}{$id}{inlay} = $self->{chat}->new_switchable_inlay (
816 exists $self->{challenge}{""}
817 ? "Challenge from $opponent->{name}"
818 : "Challenge to $opponent->{name}",
819 sub {
820 $self->{challenge}{$id}{inlay} = $_[0];
821 $self->draw_challenge ($id);
822 },
823 1 || !exists $self->{challenge}{""} # only open when not offerer
824 );
557} 825}
558 826
5591; 8271;
560 828

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines