… | |
… | |
21 | |
21 | |
22 | $self->{set} = sub { }; |
22 | $self->{set} = sub { }; |
23 | $self->{format} = sub { "???" }; |
23 | $self->{format} = sub { "???" }; |
24 | } |
24 | } |
25 | |
25 | |
|
|
26 | sub FINALIZE_INSTANCE { |
|
|
27 | my $self = shift; |
|
|
28 | |
|
|
29 | $self->stop; |
|
|
30 | } |
|
|
31 | |
26 | sub configure { |
32 | sub configure { |
27 | my ($self, $timesys, $main, $interval, $count) = @_; |
33 | my ($self, $timesys, $main, $interval, $count) = @_; |
28 | |
34 | |
29 | if ($timesys == TIMESYS_ABSOLUTE) { |
35 | if ($timesys == TIMESYS_ABSOLUTE) { |
30 | $self->{set} = sub { $self->{time} = $_[0] }; |
|
|
31 | $self->{format} = sub { |
36 | $self->{format} = sub { |
32 | if ($_[0] <= 0) { |
37 | if ($_[0] <= 0) { |
33 | "TIMEOUT"; |
38 | "TIMEOUT"; |
34 | } else { |
39 | } else { |
35 | util::format_time $_[0]; |
40 | util::format_time $_[0]; |
36 | } |
41 | } |
37 | }; |
42 | }; |
38 | |
43 | |
39 | } elsif ($timesys == TIMESYS_BYO_YOMI) { |
44 | } elsif ($timesys == TIMESYS_BYO_YOMI) { |
40 | my $low = $interval * $count; |
45 | my $low = $interval * $count; |
41 | |
|
|
42 | $self->{set} = sub { $self->{time} = $_[0] }; |
|
|
43 | |
46 | |
44 | $self->{format} = sub { |
47 | $self->{format} = sub { |
45 | if ($_[0] <= 0) { |
48 | if ($_[0] <= 0) { |
46 | "TIMEOUT"; |
49 | "TIMEOUT"; |
47 | } elsif ($_[0] > $low) { |
50 | } elsif ($_[0] > $low) { |
… | |
… | |
52 | ($_[0] - 1) / $interval; |
55 | ($_[0] - 1) / $interval; |
53 | } |
56 | } |
54 | }; |
57 | }; |
55 | |
58 | |
56 | } elsif ($timesys == TIMESYS_CANADIAN) { |
59 | } elsif ($timesys == TIMESYS_CANADIAN) { |
57 | $self->{set} = sub { $self->{time} = $_[0]; $self->{moves} = $_[1] }; |
|
|
58 | |
|
|
59 | $self->{format} = sub { |
60 | $self->{format} = sub { |
60 | if ($_[0] <= 0) { |
61 | if ($_[0] <= 0) { |
61 | "TIMEOUT"; |
62 | "TIMEOUT"; |
62 | } elsif (!$self->{moves}) { |
63 | } elsif (!$self->{moves}) { |
63 | util::format_time $_[0] - $low; |
64 | util::format_time $_[0] - $low; |
… | |
… | |
73 | } |
74 | } |
74 | }; |
75 | }; |
75 | |
76 | |
76 | } else { |
77 | } else { |
77 | # none, or unknown |
78 | # none, or unknown |
78 | $self->{set} = sub { }; |
|
|
79 | $self->{format} = sub { "-" } |
79 | $self->{format} = sub { "-" } |
80 | } |
80 | } |
81 | } |
81 | } |
82 | |
82 | |
83 | sub refresh { |
83 | sub refresh { |
… | |
… | |
92 | |
92 | |
93 | $timer - int $timer; |
93 | $timer - int $timer; |
94 | } |
94 | } |
95 | |
95 | |
96 | sub set_time { |
96 | sub set_time { |
97 | my ($self, $time, $moves) = @_; |
97 | my ($self, $start, $time, $moves) = @_; |
98 | |
98 | |
99 | # we ignore requests to re-set the time of a running clock. |
99 | $self->{time} = $time; |
100 | # this is the easiest way to ensure that commentary etc. |
100 | $self->{moves} = $moves; |
101 | # doesn't re-set the clock. yes, this is frickle design, |
101 | |
102 | # but I think the protocol is to blame here, which gives |
102 | if ($start) { |
103 | # very little time information. (cgoban2 also has had quite |
103 | $self->{start} = $start; |
104 | # a lot of small time update problems...) |
104 | $self->start; |
105 | unless ($self->{timeout}) { |
105 | } else { |
106 | $self->{set}->($time, $moves); |
106 | $self->stop; |
107 | $self->refresh ($self->{start}); |
107 | $self->refresh ($self->{start}); |
108 | } |
108 | } |
109 | } |
109 | } |
110 | |
110 | |
111 | sub start { |
111 | sub start { |
112 | my ($self, $when) = @_; |
112 | my ($self) = @_; |
113 | |
113 | |
114 | $self->stop; |
114 | $self->stop; |
115 | |
|
|
116 | $self->{start} = $when; |
|
|
117 | |
115 | |
118 | my $timeout; $timeout = sub { |
116 | my $timeout; $timeout = sub { |
119 | my $next = $self->refresh (Time::HiRes::time) * 1000; |
117 | my $next = $self->refresh (Time::HiRes::time) * 1000; |
120 | $next += 1000 if $next < 0; |
118 | $next += 1000 if $next < 0; |
121 | $self->{timeout} = add Glib::Timeout $next, $timeout; |
119 | $self->{timeout} = add Glib::Timeout $next, $timeout; |
… | |
… | |
185 | |
183 | |
186 | $self->{info}->set_text ("$captures pris."); |
184 | $self->{info}->set_text ("$captures pris."); |
187 | } |
185 | } |
188 | |
186 | |
189 | sub set_timer { |
187 | sub set_timer { |
190 | my ($self, $when, $time, $moves) = @_; |
188 | my ($self, $start, $time, $moves) = @_; |
191 | |
189 | |
192 | $self->{clock}->stop unless $when; |
|
|
193 | $self->{clock}->set_time ($time, $moves); |
190 | $self->{clock}->set_time ($start, $time, $moves); |
194 | $self->{clock}->start ($when) if $when; |
|
|
195 | } |
191 | } |
196 | |
192 | |
197 | package game; |
193 | package game; |
198 | |
194 | |
199 | use Scalar::Util qw(weaken); |
195 | use Scalar::Util qw(weaken); |
… | |
… | |
393 | dead => !($self->{cur_board}{board}[$_[0]][$_[1]] & MARK_GRAYED), |
389 | dead => !($self->{cur_board}{board}[$_[0]][$_[1]] & MARK_GRAYED), |
394 | ); |
390 | ); |
395 | } |
391 | } |
396 | }; |
392 | }; |
397 | |
393 | |
398 | } elsif (1 - $self->{colour} == $self->{cur_board}{last}) { |
394 | } elsif ($self->{colour} == $self->{whosemove}) { |
399 | # normal move |
395 | # normal move |
400 | $self->{button_pass}->set (label => "Pass", sensitive => 1, visible => 1); |
396 | $self->{button_pass}->set (label => "Pass", sensitive => 1, visible => 1); |
401 | $self->{board}->set (cursor => sub { |
397 | $self->{board}->set (cursor => sub { |
402 | # if is_valid_move oder so#TODO# |
398 | # if is_valid_move oder so#TODO# |
403 | $_[0] & (MARK_B | MARK_W) |
399 | $_[0] & (MARK_B | MARK_W) |
… | |
… | |
410 | delete $self->{board_click}; |
406 | delete $self->{board_click}; |
411 | $self->{button_pass}->sensitive (0); |
407 | $self->{button_pass}->sensitive (0); |
412 | }; |
408 | }; |
413 | } else { |
409 | } else { |
414 | $self->{button_pass}->set (label => "Pass", sensitive => 0, visible => 1); |
410 | $self->{button_pass}->set (label => "Pass", sensitive => 0, visible => 1); |
|
|
411 | $self->{board}->set (cursor => undef); |
415 | } |
412 | } |
416 | } else { |
413 | } else { |
417 | $self->{button_undo}->hide; |
414 | $self->{button_undo}->hide; |
418 | $self->{button_resign}->hide; |
415 | $self->{button_resign}->hide; |
419 | $self->{button_pass}->hide; |
416 | $self->{button_pass}->hide; |
… | |
… | |
428 | my $running = $self->{showmove} == @{$self->{path}} && !$self->{teacher}; |
425 | my $running = $self->{showmove} == @{$self->{path}} && !$self->{teacher}; |
429 | |
426 | |
430 | for my $colour (COLOUR_BLACK, COLOUR_WHITE) { |
427 | for my $colour (COLOUR_BLACK, COLOUR_WHITE) { |
431 | my $t = $timers->[$colour]; |
428 | my $t = $timers->[$colour]; |
432 | $self->{userpanel}[$colour]->set_timer ( |
429 | $self->{userpanel}[$colour]->set_timer ( |
433 | $running && $self->{lastmove_colour} == 1 - $colour && $t->[0], |
430 | $running && $colour == $self->{whosemove} && $t->[0], |
434 | $t->[1], $t->[2]); |
431 | $t->[1] || $self->{rules}{time} |
|
|
432 | + ($self->{rules}{timesys} == TIMESYS_BYO_YOMI |
|
|
433 | && $self->{rules}{interval} * $self->{rules}{count}), |
|
|
434 | $t->[2] || $self->{rules}{count}); |
435 | } |
435 | } |
436 | } |
436 | } |
437 | |
437 | |
438 | sub update_board { |
438 | sub update_board { |
439 | my ($self) = @_; |
439 | my ($self) = @_; |
… | |
… | |
444 | $self->{cur_board} = new KGS::Game::Board $self->{size}; |
444 | $self->{cur_board} = new KGS::Game::Board $self->{size}; |
445 | $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $self->{showmove} - 1]]); |
445 | $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $self->{showmove} - 1]]); |
446 | |
446 | |
447 | $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_]) |
447 | $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_]) |
448 | for COLOUR_WHITE, COLOUR_BLACK; |
448 | for COLOUR_WHITE, COLOUR_BLACK; |
|
|
449 | |
|
|
450 | if ($self->{rules}{ruleset} == RULESET_JAPANESE) { |
|
|
451 | if ($self->{curnode}{move} == 0) { |
|
|
452 | $self->{whosemove} = $self->{handicap} ? COLOUR_WHITE : COLOUR_BLACK; |
|
|
453 | } else { |
|
|
454 | $self->{whosemove} = 1 - $self->{cur_board}{last}; |
|
|
455 | } |
|
|
456 | } else { |
|
|
457 | # Chinese, Aga, NZ all have manual placement |
|
|
458 | if ($self->{curnode}{move} < $self->{handicap}) { |
|
|
459 | $self->{whosemove} = COLOUR_BLACK; |
|
|
460 | } elsif ($self->{curnode}{move} == $self->{handicap}) { |
|
|
461 | $self->{whosemove} = COLOUR_WHITE; |
|
|
462 | } else { |
|
|
463 | $self->{whosemove} = 1 - $self->{cur_board}{last}; |
|
|
464 | } |
|
|
465 | } |
|
|
466 | |
|
|
467 | my $start_time = $self->{rules}{time}; |
449 | |
468 | |
450 | if ($self->{showmove} == @{$self->{path}}) { |
469 | if ($self->{showmove} == @{$self->{path}}) { |
451 | $self->{timers} = [ |
470 | $self->{timers} = [ |
452 | [$self->{lastmove_time}, @{$self->{cur_board}{timer}[0]}], |
471 | [$self->{lastmove_time}, @{$self->{cur_board}{timer}[0]}], |
453 | [$self->{lastmove_time}, @{$self->{cur_board}{timer}[1]}], |
472 | [$self->{lastmove_time}, @{$self->{cur_board}{timer}[1]}], |
… | |
… | |
477 | $self->update_cursor; |
496 | $self->update_cursor; |
478 | } |
497 | } |
479 | |
498 | |
480 | sub event_update_tree { |
499 | sub event_update_tree { |
481 | my ($self) = @_; |
500 | my ($self) = @_; |
|
|
501 | |
|
|
502 | (delete $self->{undo_inlay})->clear |
|
|
503 | if $self->{undo_inlay}; |
482 | |
504 | |
483 | $self->{path} = $self->get_path; |
505 | $self->{path} = $self->get_path; |
484 | |
506 | |
485 | if ($self->{moveadj}) { |
507 | if ($self->{moveadj}) { |
486 | my $upper = $self->{moveadj}->upper; |
508 | my $upper = $self->{moveadj}->upper; |
… | |
… | |
550 | } |
572 | } |
551 | |
573 | |
552 | sub event_move { |
574 | sub event_move { |
553 | my ($self, $pass) = @_; |
575 | my ($self, $pass) = @_; |
554 | sound::play 1, $pass ? "pass" : "move"; |
576 | sound::play 1, $pass ? "pass" : "move"; |
555 | |
|
|
556 | if ($self->{undo_inlay}) { |
|
|
557 | (delete $self->{undo_inlay})->clear; |
|
|
558 | } |
|
|
559 | } |
577 | } |
560 | |
578 | |
561 | sub event_update_game { |
579 | sub event_update_game { |
562 | my ($self) = @_; |
580 | my ($self) = @_; |
563 | |
581 | |
… | |
… | |
734 | $inlay->{count}++; |
752 | $inlay->{count}++; |
735 | |
753 | |
736 | $inlay->clear; |
754 | $inlay->clear; |
737 | $inlay->append_text ("\n<undo>Undo requested ($inlay->{count} times)</undo>\n"); |
755 | $inlay->append_text ("\n<undo>Undo requested ($inlay->{count} times)</undo>\n"); |
738 | $inlay->append_button ("Grant", sub { |
756 | $inlay->append_button ("Grant", sub { |
739 | $inlay->clear; |
757 | (delete $self->{undo_inlay})->clear; |
740 | $self->send (grant_undo => channel => $self->{channel}); |
758 | $self->send (grant_undo => channel => $self->{channel}); |
741 | }); |
759 | }); |
742 | $inlay->append_button ("Ignore", sub { |
760 | $inlay->append_button ("Ignore", sub { |
743 | $inlay->clear; |
761 | $inlay->clear; |
744 | $inlay->{ignore} = 1; |
762 | $inlay->{ignore} = 1; |
… | |
… | |
763 | my $rules = $info->{rules}; |
781 | my $rules = $info->{rules}; |
764 | |
782 | |
765 | my $as_black = $info->{black}{name} eq $self->{conn}{name} ? 1 : 0;; |
783 | my $as_black = $info->{black}{name} eq $self->{conn}{name} ? 1 : 0;; |
766 | my $opponent = $as_black ? $info->{white} : $info->{black}; |
784 | my $opponent = $as_black ? $info->{white} : $info->{black}; |
767 | |
785 | |
768 | my ($size, $time, $interval, $count); |
786 | my ($size, $time, $interval, $count, $type); |
769 | |
787 | |
770 | if (!$self->{channel}) { |
788 | if (!$self->{channel}) { |
771 | $inlay->append_text ("\nNotes: "); |
789 | $inlay->append_text ("\nNotes: "); |
772 | $inlay->append_entry (\$info->{notes}, 20, ""); |
790 | $inlay->append_entry (\$info->{notes}, 20, ""); |
773 | $inlay->append_text ("\nGlobal Offer: "); |
791 | $inlay->append_text ("\nGlobal Offer: "); |
… | |
… | |
778 | } else { |
796 | } else { |
779 | $inlay->append_text ("\nNotes: " . util::toxml $info->{notes}); |
797 | $inlay->append_text ("\nNotes: " . util::toxml $info->{notes}); |
780 | } |
798 | } |
781 | |
799 | |
782 | $inlay->append_text ("\nType: "); |
800 | $inlay->append_text ("\nType: "); |
783 | $inlay->append_optionmenu ( |
801 | $type = $inlay->append_optionmenu ( |
784 | \$info->{gametype}, |
802 | \$info->{gametype}, |
785 | GAMETYPE_DEMONSTRATION , "Demonstration", |
803 | GAMETYPE_DEMONSTRATION , "Demonstration (not yet)", |
786 | GAMETYPE_DEMONSTRATION | GAMETYPE_PRIVATE, "Demonstration (P)", |
804 | GAMETYPE_DEMONSTRATION | GAMETYPE_PRIVATE, "Demonstration (P) (not yet)", |
787 | GAMETYPE_TEACHING , "Teaching", |
805 | GAMETYPE_TEACHING , "Teaching (not yet)", |
788 | GAMETYPE_TEACHING | GAMETYPE_PRIVATE, "Teaching (P)", |
806 | GAMETYPE_TEACHING | GAMETYPE_PRIVATE, "Teaching (P) (not yet)", |
789 | GAMETYPE_SIMUL , "Simul (not yet!)", |
807 | GAMETYPE_SIMUL , "Simul (not yet!)", |
790 | GAMETYPE_FREE , "Free", |
808 | GAMETYPE_FREE , "Free", |
791 | GAMETYPE_RATED , "Rated", |
809 | GAMETYPE_RATED , "Rated", |
792 | sub { |
810 | sub { |
793 | $size->set_history (2) if $_[0] eq GAMETYPE_RATED; |
811 | $size->set_history (2) if $_[0] eq GAMETYPE_RATED; |
… | |
… | |
817 | RULESET_NEW_ZEALAND, "New Zealand", |
835 | RULESET_NEW_ZEALAND, "New Zealand", |
818 | ); |
836 | ); |
819 | |
837 | |
820 | $inlay->append_text ("\nSize: "); |
838 | $inlay->append_text ("\nSize: "); |
821 | $size = $inlay->append_optionmenu ( |
839 | $size = $inlay->append_optionmenu ( |
|
|
840 | \$info->{rules}{size}, |
822 | \$info->{rules}{size}, 9 => 9, 13 => 13, 19 => 19, map +($_, $_), 2..38 |
841 | (9 => 9, 13 => 13, 19 => 19, map +($_, $_), 2..38), |
|
|
842 | sub { |
|
|
843 | $type->set_history (5) # reset to free |
|
|
844 | if $_[0] != 19 && $info->{gametype} == GAMETYPE_RATED; |
|
|
845 | }, |
823 | ); |
846 | ); |
824 | |
847 | |
825 | if ($self->{channel}) { |
848 | if ($self->{channel}) { |
826 | $inlay->append_text ("\nHandicap: "); |
849 | $inlay->append_text ("\nHandicap: "); |
827 | $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9); |
850 | $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9); |