… | |
… | |
71 | } |
71 | } |
72 | |
72 | |
73 | sub refresh { |
73 | sub refresh { |
74 | my ($self, $timestamp) = @_; |
74 | my ($self, $timestamp) = @_; |
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 $timer2 = int $timer + 0.4; |
|
|
80 | |
|
|
81 | if ($timer2 <= 0) { |
|
|
82 | $timer2 = 0 if $timer2 < 0; |
|
|
83 | $self->set_text ("TIME OVER"); |
|
|
84 | } else { |
80 | $self->set_text ($self->{format}->(int ($timer + 0.4))); |
85 | $self->set_text ($self->{format}->($timer2)); |
|
|
86 | } |
81 | |
87 | |
82 | $timer - int $timer; |
88 | $timer - int $timer; |
83 | } |
89 | } |
84 | |
90 | |
85 | sub set_time { |
91 | sub set_time { |
86 | my ($self, $time) = @_; |
92 | my ($self, $time, $moves) = @_; |
87 | |
93 | |
88 | # we ignore requests to re-set the time of a running clock. |
94 | # we ignore requests to re-set the time of a running clock. |
89 | # this is the easiest way to ensure that commentary etc. |
95 | # this is the easiest way to ensure that commentary etc. |
90 | # doesn't re-set the clock. yes, this is frickle design, |
96 | # doesn't re-set the clock. yes, this is frickle design, |
91 | # but I think the protocol is to blame here, which gives |
97 | # but I think the protocol is to blame here, which gives |
92 | # very little time information. (cgoban2 also has had quite |
98 | # very little time information. (cgoban2 also has had quite |
93 | # a lot of small time update problems...) |
99 | # a lot of small time update problems...) |
94 | unless ($self->{timeout}) { |
100 | unless ($self->{timeout}) { |
95 | $self->{set}->($time->[0], $time->[1]); |
101 | $self->{set}->($time, $moves); |
96 | $self->refresh ($self->{start}); |
102 | $self->refresh ($self->{start}); |
97 | } |
103 | } |
98 | } |
104 | } |
99 | |
105 | |
100 | sub start { |
106 | sub start { |
… | |
… | |
167 | } |
173 | } |
168 | |
174 | |
169 | $self->{clock}->configure (@{$rules}{qw(timesys time interval count)}); |
175 | $self->{clock}->configure (@{$rules}{qw(timesys time interval count)}); |
170 | } |
176 | } |
171 | |
177 | |
172 | sub set_state { |
178 | sub set_captures { |
173 | my ($self, $captures, $timer, $when) = @_; |
179 | my ($self, $captures) = @_; |
|
|
180 | |
|
|
181 | $self->{info}->set_text ("$captures pris."); |
|
|
182 | } |
|
|
183 | |
|
|
184 | sub set_timer { |
|
|
185 | my ($self, $when, $time, $moves) = @_; |
174 | |
186 | |
175 | $self->{clock}->stop unless $when; |
187 | $self->{clock}->stop unless $when; |
176 | $self->{clock}->set_time ($timer); |
188 | $self->{clock}->set_time ($time, $moves); |
177 | $self->{clock}->start ($when) if $when; |
189 | $self->{clock}->start ($when) if $when; |
178 | |
|
|
179 | $self->{info}->set_text ("$captures pris."); |
|
|
180 | } |
190 | } |
181 | |
191 | |
182 | package game; |
192 | package game; |
183 | |
193 | |
184 | use Scalar::Util qw(weaken); |
194 | use Scalar::Util qw(weaken); |
… | |
… | |
187 | use KGS::Game::Board; |
197 | use KGS::Game::Board; |
188 | |
198 | |
189 | use Gtk2::GoBoard; |
199 | use Gtk2::GoBoard; |
190 | use Gtk2::GoBoard::Constants; |
200 | use Gtk2::GoBoard::Constants; |
191 | |
201 | |
|
|
202 | use base KGS::Game; |
|
|
203 | use base KGS::Listener::Game; |
|
|
204 | |
192 | use Glib::Object::Subclass |
205 | use Glib::Object::Subclass |
193 | Gtk2::Window; |
206 | Gtk2::Window; |
194 | |
|
|
195 | use base KGS::Listener::Game; |
|
|
196 | use base KGS::Game; |
|
|
197 | |
207 | |
198 | use POSIX qw(ceil); |
208 | use POSIX qw(ceil); |
199 | |
209 | |
200 | sub new { |
210 | sub new { |
201 | my ($self, %arg) = @_; |
211 | my ($self, %arg) = @_; |
… | |
… | |
255 | for COLOUR_WHITE, COLOUR_BLACK; |
265 | for COLOUR_WHITE, COLOUR_BLACK; |
256 | |
266 | |
257 | $vbox->pack_start ((my $buttonbox = new Gtk2::HButtonBox), 0, 1, 0); |
267 | $vbox->pack_start ((my $buttonbox = new Gtk2::HButtonBox), 0, 1, 0); |
258 | |
268 | |
259 | $buttonbox->add ($self->{button_pass} = |
269 | $buttonbox->add ($self->{button_pass} = |
260 | Gtk2::Button->Glib::Object::new (label => "Pass", sensitive => 0)); |
270 | Gtk2::Button->Glib::Object::new (label => "Pass", no_show_all => 1, visible => 0)); |
261 | $self->{button_pass}->signal_connect (clicked => sub { |
271 | $self->{button_pass}->signal_connect (clicked => sub { |
262 | $self->{board_click}->(255, 255) if $self->{board_click}; |
272 | $self->{board_click}->(255, 255) if $self->{board_click}; |
|
|
273 | }); |
|
|
274 | $buttonbox->add ($self->{button_undo} = |
|
|
275 | Gtk2::Button->Glib::Object::new (label => "Undo", no_show_all => 1, visible => 0)); |
|
|
276 | $self->{button_undo}->signal_connect (clicked => sub { |
|
|
277 | $self->send (req_undo => channel => $self->{channel}); |
|
|
278 | }); |
|
|
279 | $buttonbox->add ($self->{button_resign} = |
|
|
280 | Gtk2::Button->Glib::Object::new (label => "Resign", no_show_all => 1, visible => 0)); |
|
|
281 | $self->{button_resign}->signal_connect (clicked => sub { |
|
|
282 | $self->send (resign_game => channel => $self->{channel}, player => $self->{colour}); |
263 | }); |
283 | }); |
264 | |
284 | |
265 | $vbox->pack_start (($self->{chat} = new superchat), 1, 1, 0); |
285 | $vbox->pack_start (($self->{chat} = new superchat), 1, 1, 0); |
266 | |
286 | |
267 | $self->set_channel ($self->{channel}); |
287 | $self->set_channel ($self->{channel}); |
… | |
… | |
330 | my ($self) = @_; |
350 | my ($self) = @_; |
331 | |
351 | |
332 | my $move = int $self->{moveadj}->get_value; |
352 | my $move = int $self->{moveadj}->get_value; |
333 | my $cb; |
353 | my $cb; |
334 | |
354 | |
335 | warn sprintf "move %d spath %d\n", $move, scalar @{$self->{path}};#d# |
|
|
336 | warn "isact " . $self->is_active;#d# |
|
|
337 | warn "flags $self->{flags}\n";#d# |
|
|
338 | warn "handi $self->{handicap}\n";#d# |
|
|
339 | |
|
|
340 | my $running = $move == @{$self->{path}}; # && $self->is_active; |
355 | my $running = $move == @{$self->{path}} && $self->is_active; |
341 | |
356 | |
342 | delete $self->{board_click}; |
357 | delete $self->{board_click}; |
343 | |
358 | |
344 | if ($self->{teacher} eq $self->{app}{conn}) { |
359 | if ($self->{teacher} eq $self->{app}{conn}) { |
345 | #TODO# # teaching mode not implemented |
360 | #TODO# # teaching mode not implemented |
346 | $self->{button_pass}->set (label => "Pass", sensitive => 1, visible => 1); |
361 | $self->{button_pass}->set (label => "Pass", sensitive => 1, visible => 1); |
|
|
362 | $self->{button_undo}->hide; |
347 | $self->{button_pass}->show; |
363 | $self->{button_resign}->hide; |
348 | $self->{board}->set (cursor => undef); |
364 | $self->{board}->set (cursor => undef); |
349 | |
365 | |
350 | } elsif ($running && $self->{colour} != COLOUR_NONE) { |
366 | } elsif ($running && $self->{colour} != COLOUR_NONE) { |
351 | # during game |
367 | # during game |
|
|
368 | $self->{button_undo}->show; |
|
|
369 | $self->{button_resign}->show; |
352 | |
370 | |
353 | if ($self->{cur_board}{score}) { |
371 | if ($self->{cur_board}{score}) { |
354 | # during scoring |
372 | # during scoring |
355 | $self->{button_pass}->set (label => "Done", sensitive => 1, visible => 1); |
373 | $self->{button_pass}->set (label => "Done", sensitive => 1, visible => 1); |
356 | $self->{board}->set (cursor => sub { |
374 | $self->{board}->set (cursor => sub { |
… | |
… | |
389 | }; |
407 | }; |
390 | } else { |
408 | } else { |
391 | $self->{button_pass}->set (label => "Pass", sensitive => 0, visible => 1); |
409 | $self->{button_pass}->set (label => "Pass", sensitive => 0, visible => 1); |
392 | } |
410 | } |
393 | } else { |
411 | } else { |
|
|
412 | $self->{button_undo}->hide; |
|
|
413 | $self->{button_resign}->hide; |
394 | $self->{button_pass}->hide; |
414 | $self->{button_pass}->hide; |
395 | $self->{board}->set (cursor => undef); |
415 | $self->{board}->set (cursor => undef); |
396 | #TODO# # implement coordinate-grabbing |
416 | #TODO# # implement coordinate-grabbing |
397 | } |
417 | } |
398 | } |
418 | } |
… | |
… | |
401 | my ($self) = @_; |
421 | my ($self) = @_; |
402 | return unless $self->{path}; |
422 | return unless $self->{path}; |
403 | |
423 | |
404 | my $move = int $self->{moveadj}->get_value; |
424 | my $move = int $self->{moveadj}->get_value; |
405 | |
425 | |
406 | my $running = $move == @{$self->{path}} && !$self->{teacher}; |
|
|
407 | |
|
|
408 | $self->{board_label}->set_text ("Move " . ($move - 1)); |
426 | $self->{board_label}->set_text ("Move " . ($move - 1)); |
409 | |
427 | |
410 | $self->{cur_board} = new KGS::Game::Board $self->{size}; |
428 | $self->{cur_board} = new KGS::Game::Board $self->{size}; |
411 | $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $move - 1]]); |
429 | $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $move - 1]]); |
412 | |
430 | |
|
|
431 | $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_]) |
413 | for my $colour (COLOUR_WHITE, COLOUR_BLACK) { |
432 | for COLOUR_WHITE, COLOUR_BLACK; |
414 | $self->{userpanel}[$colour]->set_state ( |
|
|
415 | $self->{cur_board}{captures}[$colour], |
|
|
416 | $self->{cur_board}{timer}[$colour], |
|
|
417 | ($running && $self->{lastmove_colour} == !$colour) |
|
|
418 | ? $self->{lastmove_time} : 0 |
|
|
419 | ); |
|
|
420 | } |
|
|
421 | |
433 | |
422 | $self->{board}->set_board ($self->{cur_board}); |
434 | $self->{board}->set_board ($self->{cur_board}); |
423 | |
435 | |
424 | $self->update_cursor; |
436 | $self->update_cursor; |
425 | } |
437 | } |
… | |
… | |
515 | $self->{colour} = $self->player_colour ($self->{conn}{name}); |
527 | $self->{colour} = $self->player_colour ($self->{conn}{name}); |
516 | |
528 | |
517 | my $title = defined $self->{channel} |
529 | my $title = defined $self->{channel} |
518 | ? $self->owner->as_string . " " . $self->opponent_string |
530 | ? $self->owner->as_string . " " . $self->opponent_string |
519 | : "Game Window"; |
531 | : "Game Window"; |
520 | $self->set_title("KGS Game $title"); |
532 | $self->set_title ("KGS Game $title"); |
521 | $self->{title}->set_text ($title); |
533 | $self->{title}->set_text ($title); |
522 | |
534 | |
523 | $self->{user}[COLOUR_BLACK] = $self->{black}; |
535 | $self->{user}[COLOUR_BLACK] = $self->{black}; |
524 | $self->{user}[COLOUR_WHITE] = $self->{white}; |
536 | $self->{user}[COLOUR_WHITE] = $self->{white}; |
525 | |
537 | |
… | |
… | |
638 | . "\n<user>" |
650 | . "\n<user>" |
639 | . (util::toxml $self->{user}[$msg->{player}]->as_string) |
651 | . (util::toxml $self->{user}[$msg->{player}]->as_string) |
640 | . "</user> ran out of time.</infoblock>"); |
652 | . "</user> ran out of time.</infoblock>"); |
641 | } |
653 | } |
642 | |
654 | |
|
|
655 | sub event_done { |
|
|
656 | my ($self) = @_; |
|
|
657 | |
|
|
658 | if ($self->{done}[1 - $self->{colour}] && !$self->{done}[$self->{colour}]) { |
|
|
659 | $self->{chat}->append_text ("\n<infoblock><header>Done</header>" |
|
|
660 | . "\nYour opponent pressed done."); |
|
|
661 | } |
|
|
662 | } |
|
|
663 | |
643 | sub inject_final_result { |
664 | sub inject_final_result { |
644 | my ($self, $msg) = @_; |
665 | my ($self, $msg) = @_; |
645 | |
666 | |
646 | $self->{chat}->append_text ("<infoblock>\n<header>Game Over</header>" |
667 | $self->{chat}->append_text ("<infoblock>\n<header>Game Over</header>" |
647 | . "\nWhite Score " . (util::toxml $msg->{whitescore}->as_string) |
668 | . "\nWhite Score " . (util::toxml $msg->{whitescore}->as_string) |
648 | . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string) |
669 | . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string) |
649 | . "</infoblock>" |
670 | . "</infoblock>" |
650 | ); |
671 | ); |
|
|
672 | } |
|
|
673 | |
|
|
674 | sub inject_set_gametime { |
|
|
675 | my ($self, $msg) = @_; |
|
|
676 | |
|
|
677 | my $move = int $self->{moveadj}->get_value; |
|
|
678 | |
|
|
679 | my $running = $move == @{$self->{path}} && !$self->{teacher}; |
|
|
680 | |
|
|
681 | $self->{userpanel}[COLOUR_BLACK]->set_timer ( |
|
|
682 | $running && $self->{lastmove_colour} == COLOUR_WHITE && $msg->{NOW}, |
|
|
683 | $msg->{black_time}, $msg->{black_moves}); |
|
|
684 | $self->{userpanel}[COLOUR_WHITE]->set_timer ( |
|
|
685 | $running && $self->{lastmove_colour} == COLOUR_BLACK && $msg->{NOW}, |
|
|
686 | $msg->{white_time}, $msg->{white_moves}); |
651 | } |
687 | } |
652 | |
688 | |
653 | sub inject_req_undo { |
689 | sub inject_req_undo { |
654 | my ($self, $msg) = @_; |
690 | my ($self, $msg) = @_; |
655 | |
691 | |
… | |
… | |
867 | }, |
903 | }, |
868 | !exists $self->{challenge}{""} # only open when not offerer |
904 | !exists $self->{challenge}{""} # only open when not offerer |
869 | ); |
905 | ); |
870 | } |
906 | } |
871 | |
907 | |
872 | sub event_done { |
|
|
873 | my ($self) = @_; |
|
|
874 | print "FDONE($self->{doneid} $self->{done}[0],$self->{done}[1]\n";#d# |
|
|
875 | } |
|
|
876 | |
|
|
877 | 1; |
908 | 1; |
878 | |
909 | |