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.110 by root, Mon May 31 17:06:19 2004 UTC vs.
Revision 1.131 by elmex, Fri Jun 4 14:17:53 2004 UTC

1use utf8; 1use utf8;
2 2
3use Scalar::Util (); 3use Scalar::Util ();
4
5### GO CLOCK WIDGET #########################################################
4 6
5package game::goclock; 7package game::goclock;
6 8
7# Lo and Behold! I admit it! The rounding stuff etc.. in goclock 9# Lo and Behold! I admit it! The rounding stuff etc.. in goclock
8# is completely borked. 10# is completely borked.
32sub configure { 34sub configure {
33 my ($self, $timesys, $main, $interval, $count) = @_; 35 my ($self, $timesys, $main, $interval, $count) = @_;
34 36
35 if ($timesys == TIMESYS_ABSOLUTE) { 37 if ($timesys == TIMESYS_ABSOLUTE) {
36 $self->{format} = sub { 38 $self->{format} = sub {
37 if ($_[0] <= 0) { 39 if ($_[0] < 0) {
38 "TIMEOUT"; 40 "TIMEOUT";
39 } else { 41 } else {
40 util::format_time $_[0]; 42 util::format_time $_[0];
41 } 43 }
42 }; 44 };
43 45
44 } elsif ($timesys == TIMESYS_BYO_YOMI) { 46 } elsif ($timesys == TIMESYS_BYO_YOMI) {
45 my $low = $interval * $count; 47 my $low = $interval * $count;
46 48
47 $self->{format} = sub { 49 $self->{format} = sub {
48 if ($_[0] <= 0) { 50 if ($_[0] < 0) {
49 "TIMEOUT"; 51 "TIMEOUT";
50 } elsif ($_[0] > $low) { 52 } elsif ($_[0] > $low) {
51 util::format_time $_[0] - $low; 53 util::format_time $_[0] - $low;
52 } else { 54 } else {
53 sprintf "%s (%d)", 55 sprintf "%s (%d)",
56 } 58 }
57 }; 59 };
58 60
59 } elsif ($timesys == TIMESYS_CANADIAN) { 61 } elsif ($timesys == TIMESYS_CANADIAN) {
60 $self->{format} = sub { 62 $self->{format} = sub {
61 if ($_[0] <= 0) { 63 if ($_[0] < 0) {
62 "TIMEOUT"; 64 "TIMEOUT";
63 } elsif (!$self->{moves}) { 65 } elsif (!$self->{moves}) {
64 util::format_time $_[0] - $low; 66 util::format_time $_[0] - $low;
65 } else { 67 } else {
66 my $time = int (($_[0] - 1) % $interval + 1); 68 my $time = int (($_[0] - 1) % $interval + 1);
127 my ($self) = @_; 129 my ($self) = @_;
128 130
129 remove Glib::Source delete $self->{timeout} if $self->{timeout}; 131 remove Glib::Source delete $self->{timeout} if $self->{timeout};
130} 132}
131 133
134### USER PANEL ##############################################################
135
132package game::userpanel; 136package game::userpanel;
133 137
138use KGS::Constants;
139
134use Glib::Object::Subclass 140use Glib::Object::Subclass
135 Gtk2::HBox, 141 Gtk2::Frame,
136 properties => [ 142 properties => [
137 Glib::ParamSpec->IV ("colour", "colour", "User Colour", 0, 1, 0, [qw(construct-only writable)]), 143 Glib::ParamSpec->IV ("colour", "colour", "User Colour",
144 COLOUR_BLACK, COLOUR_WHITE, COLOUR_BLACK,
145 [qw(construct-only readable writable)]),
138 ]; 146 ];
139 147
140sub INIT_INSTANCE { 148sub INIT_INSTANCE {
141 my ($self) = @_; 149 my ($self) = @_;
142 150
151 $self->add ($self->{window} = my $window = new Gtk2::EventBox); # for bg
152
143 $self->add (my $vbox = new Gtk2::VBox); 153 $window->add (my $vbox = new Gtk2::VBox);
144 154
145 $vbox->add ($self->{name} = new Gtk2::Label $self->{name}); 155 $vbox->pack_start (($self->{name} = new Gtk2::Label "-"), 1, 1, 0);
146 $vbox->add ($self->{info} = new Gtk2::Label ""); 156 $vbox->pack_start (($self->{info} = new Gtk2::Label "-"), 1, 1, 0);
147 $vbox->add ($self->{clock} = new game::goclock); Scalar::Util::weaken $self->{clock}; 157 $vbox->pack_start (($self->{clock} = new game::goclock), 1, 1, 0);
148 158
149 $vbox->add ($self->{imagebox} = new Gtk2::VBox); 159 $vbox->add ($self->{imagebox} = new Gtk2::VBox);
150 160
151 $self; 161 $self;
162}
163
164sub SET_PROPERTY {
165 my ($self, $pspec, $value) = @_;
166
167 $self->{$pspec->get_name} = $value;
168
169 $self->set_name ("userpanel-$self->{colour}");
152} 170}
153 171
154sub configure { 172sub configure {
155 my ($self, $app, $user, $rules) = @_; 173 my ($self, $app, $user, $rules) = @_;
156 174
157 if ($self->{name}->get_text ne $user->as_string) { 175 if ($self->{name}->get_text ne $user->as_string) {
158 $self->{name}->set_text ($user->as_string); 176 $self->{name}->set_text ($user->as_string);
159 177
160 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children; 178 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children;
179 if (0) {
161 $self->{imagebox}->add (gtk::image_from_data undef); 180 $self->{imagebox}->add (gtk::image_from_data undef);
181 }
162 $self->{imagebox}->show_all; 182 $self->{imagebox}->show_all;
163 183
164 if ($user->has_pic) { 184 if ($user->has_pic) {
165 # the big picture... 185 # the big picture...
166 $app->userpic ($user->{name}, sub { 186 $app->userpic ($user->{name}, sub {
167 return unless $self->{imagebox}; 187 return unless $self->{imagebox};
168 188
169 if ($_[0]) { 189 if ($_[0]) {
170 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children; 190 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children;
191 if (0) {
171 $self->{imagebox}->add (gtk::image_from_data $_[0]); 192 $self->{imagebox}->add (gtk::image_from_data $_[0]);
193 }
172 $self->{imagebox}->show_all; 194 $self->{imagebox}->show_all;
173 } 195 }
174 }); 196 });
175 } 197 }
176 } 198 }
188 my ($self, $start, $time, $moves) = @_; 210 my ($self, $start, $time, $moves) = @_;
189 211
190 $self->{clock}->set_time ($start, $time, $moves); 212 $self->{clock}->set_time ($start, $time, $moves);
191} 213}
192 214
215### GAME WINDOW #############################################################
216
193package game; 217package game;
194 218
195use Scalar::Util qw(weaken); 219use Scalar::Util qw(weaken);
196 220
197use KGS::Constants; 221use KGS::Constants;
208 232
209use POSIX qw(ceil); 233use POSIX qw(ceil);
210 234
211sub new { 235sub new {
212 my ($self, %arg) = @_; 236 my ($self, %arg) = @_;
237
213 $self = $self->Glib::Object::new; 238 $self = $self->Glib::Object::new;
214 $self->{$_} = delete $arg{$_} for keys %arg; 239 $self->{$_} = delete $arg{$_} for keys %arg;
215 240
216 gtk::state $self, "game::window", undef, window_size => [600, 500]; 241 gtk::state $self, "game::window", undef, window_size => [620, 460];
242 $self->set (allow_shrink => 1);
217 243
218 $self->signal_connect (destroy => sub { 244 $self->signal_connect (destroy => sub {
219 $self->unlisten; 245 $self->unlisten;
220 delete $self->{app}{game}{$self->{channel}}; 246 delete $self->{app}{game}{$self->{channel}};
221 %{$_[0]} = (); 247 %{$_[0]} = ();
222 });#d# 248 });#d#
223 249
224 $self->add (my $hpane = new Gtk2::HPaned); 250 $self->add (my $hpane = new Gtk2::HPaned);
225 gtk::state $hpane, "game::hpane", undef, position => 500; 251 gtk::state $hpane, "game::hpane", undef, position => 420;
226 252
227 # LEFT PANE 253 # LEFT PANE
228 254
229 $hpane->pack1 (($self->{left} = new Gtk2::VBox), 1, 0); 255 $hpane->pack1 (($self->{left} = new Gtk2::VBox), 1, 0);
230 256
231 $self->{boardbox} = new Gtk2::VBox;
232
233 $hpane->pack1((my $vbox = new Gtk2::VBox), 1, 1); 257 $hpane->pack1((my $vbox = new Gtk2::VBox), 1, 1);
234 258
235 # board box (aspect/canvas) 259 # board box (aspect/canvas)
236 260
237 #$self->{boardbox}->pack_start((my $frame = new Gtk2::Frame), 0, 1, 0);
238
239 # RIGHT PANE 261 # RIGHT PANE
240 262
241 $hpane->pack2 ((my $vbox = new Gtk2::VBox), 1, 1); 263 $hpane->pack2 ((my $vbox = new Gtk2::VBox), 1, 1);
242 $hpane->set (position_set => 1); 264 $hpane->set (position_set => 1);
243 265
244 $vbox->pack_start ((my $frame = new Gtk2::Frame), 0, 1, 0); 266 $vbox->pack_start ((my $frame = new Gtk2::Frame), 0, 1, 0);
245 267
246 { 268 {
247 $frame->add (my $vbox = new Gtk2::VBox); 269 $frame->add (my $vbox = new Gtk2::VBox);
248 $vbox->add ($self->{title} = new Gtk2::Label $title); 270 $vbox->add ($self->{title} = new Gtk2::Label "-");
249 271
250 $vbox->add (my $hbox = new Gtk2::HBox); 272 $vbox->add (my $hbox = new Gtk2::HBox);
251 273
252 $hbox->pack_start (($self->{board_label} = new Gtk2::Label), 0, 1, 0); 274 $hbox->pack_start (($self->{board_label} = new Gtk2::Label), 0, 0, 0);
253 275
254 $self->{moveadj} = new Gtk2::Adjustment 1, 1, 1, 1, 5, 0; 276 $self->{moveadj} = new Gtk2::Adjustment 1, 1, 1, 1, 5, 0;
255 277
256 $hbox->pack_start ((my $scale = new Gtk2::HScale $self->{moveadj}), 1, 1, 0); 278 $hbox->pack_start ((my $scale = new Gtk2::HScale $self->{moveadj}), 1, 1, 0);
257 $scale->set_draw_value (0); 279 $scale->set_draw_value (0);
269 for COLOUR_WHITE, COLOUR_BLACK; 291 for COLOUR_WHITE, COLOUR_BLACK;
270 292
271 $vbox->pack_start ((my $buttonbox = new Gtk2::HButtonBox), 0, 1, 0); 293 $vbox->pack_start ((my $buttonbox = new Gtk2::HButtonBox), 0, 1, 0);
272 294
273 $buttonbox->add ($self->{button_pass} = 295 $buttonbox->add ($self->{button_pass} =
274 Gtk2::Button->Glib::Object::new (label => "Pass", no_show_all => 1, visible => 0)); 296 Gtk2::Button->Glib::Object::new (label => "Pass", visible => 0));
275 $self->{button_pass}->signal_connect (clicked => sub { 297 $self->{button_pass}->signal_connect (clicked => sub {
276 $self->{board_click}->(255, 255) if $self->{board_click}; 298 $self->{board_click}->(255, 255) if $self->{board_click};
277 }); 299 });
300 eval { $self->{button_pass}->set (no_show_all => 1) }; # workaround for gtk+-2.2
278 $buttonbox->add ($self->{button_undo} = 301 $buttonbox->add ($self->{button_undo} =
279 Gtk2::Button->Glib::Object::new (label => "Undo", no_show_all => 1, visible => 0)); 302 Gtk2::Button->Glib::Object::new (label => "Undo", visible => 0));
280 $self->{button_undo}->signal_connect (clicked => sub { 303 $self->{button_undo}->signal_connect (clicked => sub {
281 $self->send (req_undo => channel => $self->{channel}); 304 $self->send (req_undo => channel => $self->{channel});
282 }); 305 });
306 eval { $self->{button_undo}->set (no_show_all => 1) }; # workaround for gtk+-2.2
283 $buttonbox->add ($self->{button_resign} = 307 $buttonbox->add ($self->{button_resign} =
284 Gtk2::Button->Glib::Object::new (label => "Resign", no_show_all => 1, visible => 0)); 308 Gtk2::Button->Glib::Object::new (label => "Resign", visible => 0));
285 $self->{button_resign}->signal_connect (clicked => sub { 309 $self->{button_resign}->signal_connect (clicked => sub {
286 $self->send (resign_game => channel => $self->{channel}, player => $self->{colour}); 310 $self->send (resign_game => channel => $self->{channel}, player => $self->{colour});
287 }); 311 });
312 eval { $self->{button_resign}->set (no_show_all => 1) }; # workaround for gtk+-2.2
288 313
289 $vbox->pack_start (($self->{chat} = new superchat), 1, 1, 0); 314 $vbox->pack_start (($self->{chat} = new chat), 1, 1, 0);
290 315
291 $self->set_channel ($self->{channel}); 316 $self->set_channel ($self->{channel});
292 317
293 $self->show_all; 318 $self->show_all;
294 319
316 } 341 }
317 }); 342 });
318 } 343 }
319} 344}
320 345
346### JOIN/LEAVE ##############################################################
347
348sub join {
349 my ($self) = @_;
350 return if $self->{joined};
351
352 $self->SUPER::join;
353}
354
355sub part {
356 my ($self) = @_;
357
358 $self->hide;
359 $self->SUPER::part;
360}
361
362sub event_join {
363 my ($self) = @_;
364
365 $self->SUPER::event_join (@_);
366 $self->init_tree;
367 $self->event_update_game;
368}
369
370sub event_part {
371 my ($self) = @_;
372
373 $self->SUPER::event_part;
374 $self->destroy;
375}
376
377sub event_quit {
378 my ($self) = @_;
379
380 $self->SUPER::event_quit;
381 $self->destroy;
382}
383
384### USERS ###################################################################
385
386sub draw_users {
387 my ($self, $inlay) = @_;
388
389 for (sort keys %{$self->{users}}) {
390 $inlay->append_text (" <user>" . $self->{users}{$_}->as_string . "</user>");
391 }
392}
393
321sub event_update_users { 394sub event_update_users {
322 my ($self, $add, $update, $remove) = @_; 395 my ($self, $add, $update, $remove) = @_;
323 396
324# $self->{userlist}->update ($add, $update, $remove); 397# $self->{userlist}->update ($add, $update, $remove);
325 398
332 $important{$self->{black}{name}}++; 405 $important{$self->{black}{name}}++;
333 $important{$self->{white}{name}}++; 406 $important{$self->{white}{name}}++;
334 $important{$self->{owner}{name}}++; 407 $important{$self->{owner}{name}}++;
335 408
336 if (my @users = grep $important{$_->{name}}, @$add) { 409 if (my @users = grep $important{$_->{name}}, @$add) {
337 $self->{chat}->append_text ("\n<header>Joins:</header>"); 410 $self->{chat}->append_text ("\n<leader>Joins:</leader>");
338 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users; 411 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users;
339 } 412 }
340 if (my @users = grep $important{$_->{name}}, @$remove) { 413 if (my @users = grep $important{$_->{name}}, @$remove) {
341 $self->{chat}->append_text ("\n<header>Parts:</header>"); 414 $self->{chat}->append_text ("\n<leader>Parts:</leader>");
342 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users; 415 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users;
343 } 416 }
344} 417}
345 418
346sub join { 419### GAME INFO ###############################################################
420
421sub draw_setup {
347 my ($self) = @_; 422 my ($self, $inlay) = @_;
423
348 return if $self->{joined}; 424 return unless $self->{joined};
349 425
350 $self->SUPER::join; 426 my $rules = $self->{rules};
427
428 my $text = "";
429
430 $text .= "\nTeacher: <user>" . (util::toxml $self->{teacher}) . "</user>"
431 if $self->{teacher};
432
433 $text .= "\nOwner: <user>" . (util::toxml $self->{owner}->as_string) . "</user>"
434 if $self->{owner}->is_valid;
435
436 if ($self->is_inprogress) {
437 $text .= "\nPlayers: <user>" . (util::toxml $self->{white}->as_string) . "</user>"
438 . " vs. <user>" . (util::toxml $self->{black}->as_string) . "</user>";
439 }
440 $text .= "\nType: " . util::toxml $gametype{$self->type};
441
442 $text .= "\nRuleset: " . $ruleset{$rules->{ruleset}};
443
444 $text .= "\nTime: ";
445
446 if ($rules->{timesys} == TIMESYS_NONE) {
447 $text .= "UNLIMITED";
448 } elsif ($rules->{timesys} == TIMESYS_ABSOLUTE) {
449 $text .= util::format_time $rules->{time};
450 $text .= " ABS";
451 } elsif ($rules->{timesys} == TIMESYS_BYO_YOMI) {
452 $text .= util::format_time $rules->{time};
453 $text .= sprintf " + %s (%d) BY", util::format_time $rules->{interval}, $rules->{count};
454 } elsif ($rules->{timesys} == TIMESYS_CANADIAN) {
455 $text .= util::format_time $rules->{time};
456 $text .= sprintf " + %s/%d CAN", util::format_time $rules->{interval}, $rules->{count};
457 }
458
459 $text .= "\nFlags:";
460 $text .= " private" if $self->is_private;
461 $text .= " started" if $self->is_inprogress;
462 $text .= " adjourned" if $self->is_adjourned;
463 $text .= " scored" if $self->is_scored;
464 $text .= " saved" if $self->is_saved;
465
466 if ($self->is_inprogress) {
467 $text .= "\nHandicap: " . $self->{handicap};
468 $text .= "\nKomi: " . $self->{komi};
469 $text .= "\nSize: " . $self->size_string;
470 }
471
472 if ($self->is_scored) {
473 $text .= "\nResult: " . $self->score_string;
474 }
475
476 $inlay->append_text ("<infoblock>$text</infoblock>");
477
478}
479
480sub event_update_game {
481 my ($self) = @_;
482
483 $self->SUPER::event_update_game;
484
485 return unless $self->{joined};
486
487 $self->{colour} = $self->player_colour ($self->{conn}{name});
488
489 $self->{user}[COLOUR_BLACK] = $self->{black};
490 $self->{user}[COLOUR_WHITE] = $self->{white};
491
492 # show board
493 if ($self->is_inprogress) {
494 if (!$self->{board}) {
495 $self->{left}->add ($self->{board} = new Gtk2::GoBoard size => $self->{size});
496 $self->{board}->signal_connect (button_release => sub {
497 return unless $self->{cur_board};
498 if ($_[1] == 1) {
499 $self->{board_click}->($_[2], $_[3]) if $self->{board_click};
500 }
501 });
502 $self->{board}->show_all;
503 }
504 if (my $ch = delete $self->{challenge}) {
505 $_->{inlay}->destroy for values %$ch;
506 }
507 $self->update_cursor;
508 }
509
510 my $title = defined $self->{channel}
511 ? $self->owner->as_string . " " . $self->opponent_string
512 : "Game Window";
513 $self->set_title ("KGS Game $title");
514 $self->{title}->set_text ($title); # title gets redrawn wrongly
515
516 $self->{rules_inlay}->refresh;
517
518 if (exists $self->{teacher}) {
519 $self->{teacher_inlay} ||= $self->{chat}->new_inlay;
520 $self->{teacher_inlay}->clear;
521 $self->{teacher_inlay}->append_text ("\n<header>Teacher:</header> <user>"
522 . (util::toxml $self->{teacher}) . "</user>");
523 } elsif ($self->{teacher_inlay}) {
524 (delete $self->{teacher_inlay})->clear;
525 }
526
527 $self->update_cursor;
528}
529
530sub event_update_rules {
531 my ($self, $rules) = @_;
532
533 $self->{rules} = $rules;
534
535 if ($self->{user}) {
536 # todo. gets drawn wrongly
537
538 $self->{userpanel}[$_]->configure ($self->{app}, $self->{user}[$_], $rules)
539 for COLOUR_BLACK, COLOUR_WHITE;
540 }
541
542 sound::play 3, "gamestart";
543 $self->{rules_inlay}->refresh;
544}
545
546### BOARD DISPLAY ###########################################################
547
548sub update_timers {
549 my ($self, $timers) = @_;
550
551 my $running = $self->{showmove} == @{$self->{path}} && !$self->{teacher};
552
553 for my $colour (COLOUR_BLACK, COLOUR_WHITE) {
554 my $t = $timers->[$colour];
555 $self->{userpanel}[$colour]->set_timer (
556 $running && $colour == $self->{whosemove} && $t->[0],
557 $t->[1] || $self->{rules}{time}
558 + ($self->{rules}{timesys} == TIMESYS_BYO_YOMI
559 && $self->{rules}{interval} * $self->{rules}{count}),
560 $t->[2]);
561 }
562}
563
564sub inject_set_gametime {
565 my ($self, $msg) = @_;
566
567 $self->{timers} = [
568 [$msg->{NOW}, $msg->{black_time}, $msg->{black_moves}],
569 [$msg->{NOW}, $msg->{white_time}, $msg->{white_moves}],
570 ];
571
572 $self->update_timers ($self->{timers})
573 if $self->{showmove} == @{$self->{path}};
351} 574}
352 575
353sub update_cursor { 576sub update_cursor {
354 my ($self) = @_; 577 my ($self) = @_;
578
579 return unless $self->{cur_board};
580
581 if ($self->{rules}{ruleset} == RULESET_JAPANESE) {
582 if ($self->{curnode}{move} == 0) {
583 $self->{whosemove} = $self->{handicap} ? COLOUR_WHITE : COLOUR_BLACK;
584 } else {
585 $self->{whosemove} = 1 - $self->{cur_board}{last};
586 }
587 } else {
588 # Chinese, Aga, NZ all have manual placement
589 if ($self->{curnode}{move} < $self->{handicap}) {
590 $self->{whosemove} = COLOUR_BLACK;
591 } elsif ($self->{curnode}{move} == $self->{handicap}) {
592 $self->{whosemove} = $self->{handicap} ? COLOUR_WHITE : COLOUR_BLACK;
593 } else {
594 $self->{whosemove} = 1 - $self->{cur_board}{last};
595 }
596 }
355 597
356 my $running = $self->{showmove} == @{$self->{path}} && $self->is_active; 598 my $running = $self->{showmove} == @{$self->{path}} && $self->is_active;
357 599
358 delete $self->{board_click}; 600 delete $self->{board_click};
359 601
360 if ($self->{teacher} eq $self->{app}{conn}) { 602 if ($self->{teacher} eq $self->{app}{conn}) {
361 #TODO# # teaching mode not implemented 603 #TODO# # teaching mode not implemented
362 $self->{button_pass}->set (label => "Pass", visible => 1, sensitive => 1); 604 $self->{button_pass}->set (label => "Pass", sensitive => 1);
605 $self->{button_pass}->show;
363 $self->{button_undo}->hide; 606 $self->{button_undo}->hide;
364 $self->{button_resign}->hide; 607 $self->{button_resign}->hide;
365 $self->{board}->set (cursor => undef); 608 $self->{board}->set (cursor => undef);
366 609
367 } elsif ($running && $self->{colour} != COLOUR_NONE) { 610 } elsif ($running && $self->{colour} != COLOUR_NONE) {
369 $self->{button_undo}->show; 612 $self->{button_undo}->show;
370 $self->{button_resign}->show; 613 $self->{button_resign}->show;
371 614
372 if ($self->{cur_board}{score}) { 615 if ($self->{cur_board}{score}) {
373 # during scoring 616 # during scoring
374 $self->{button_pass}->set (label => "Done", visible => 1, sensitive => 1); 617 $self->{button_pass}->set (label => "Done", sensitive => 1);
618 $self->{button_pass}->show;
375 $self->{board}->set (cursor => sub { 619 $self->{board}->set (cursor => sub {
376 $_[0] & (MARK_B | MARK_W) 620 $_[0] & (MARK_B | MARK_W)
377 ? $_[0] ^ MARK_GRAYED 621 ? $_[0] ^ MARK_GRAYED
378 : $_[0]; 622 : $_[0];
379 }); 623 });
391 } 635 }
392 }; 636 };
393 637
394 } elsif ($self->{colour} == $self->{whosemove}) { 638 } elsif ($self->{colour} == $self->{whosemove}) {
395 # normal move 639 # normal move
396 $self->{button_pass}->set (label => "Pass", visible => 1, sensitive => 1); 640 $self->{button_pass}->set (label => "Pass", sensitive => 1);
641 $self->{button_pass}->show;
397 $self->{board}->set (cursor => sub { 642 $self->{board}->set (cursor => sub {
398 # if is_valid_move oder so#TODO# 643 $self->{cur_board}
399 $_[0] & (MARK_B | MARK_W) 644 && $self->{cur_board}->is_valid_move ($self->{colour}, $_[1], $_[2],
400 ? $_[0] 645 $self->{rules}{ruleset} == RULESET_NEW_ZEALAND)
401 : $_[0] | MARK_GRAYED | ($self->{colour} == COLOUR_WHITE ? MARK_W : MARK_B); 646 ? $_[0] | MARK_GRAYED | ($self->{colour} == COLOUR_WHITE ? MARK_W : MARK_B)
647 : $_[0];
402 }); 648 });
403 $self->{board_click} = sub { 649 $self->{board_click} = sub {
650 return unless
651 $self->{cur_board}->is_valid_move ($self->{colour}, $_[0], $_[1],
652 $self->{rules}{ruleset} == RULESET_NEW_ZEALAND);
404 $self->send (game_move => channel => $self->{channel}, x => $_[0], y => $_[1]); 653 $self->send (game_move => channel => $self->{channel}, x => $_[0], y => $_[1]);
405 $self->{board}->set (cursor => undef); 654 $self->{board}->set (cursor => undef);
406 delete $self->{board_click}; 655 delete $self->{board_click};
407 $self->{button_pass}->sensitive (0); 656 $self->{button_pass}->sensitive (0);
408 }; 657 };
409 } else { 658 } else {
410 $self->{button_pass}->set (label => "Pass", sensitive => 0, visible => 1); 659 $self->{button_pass}->set (label => "Pass", sensitive => 0);
660 $self->{button_pass}->show;
411 $self->{board}->set (cursor => undef); 661 $self->{board}->set (cursor => undef);
412 } 662 }
413 } else { 663 } else {
414 $self->{button_undo}->hide; 664 $self->{button_undo}->hide;
415 $self->{button_resign}->hide; 665 $self->{button_resign}->hide;
417 $self->{board}->set (cursor => undef); 667 $self->{board}->set (cursor => undef);
418 #TODO# # implement coordinate-grabbing 668 #TODO# # implement coordinate-grabbing
419 } 669 }
420} 670}
421 671
422sub update_timers {
423 my ($self, $timers) = @_;
424
425 my $running = $self->{showmove} == @{$self->{path}} && !$self->{teacher};
426
427 for my $colour (COLOUR_BLACK, COLOUR_WHITE) {
428 my $t = $timers->[$colour];
429 $self->{userpanel}[$colour]->set_timer (
430 $running && $colour == $self->{whosemove} && $t->[0],
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 }
436}
437
438sub update_board { 672sub update_board {
439 my ($self) = @_; 673 my ($self) = @_;
674
440 return unless $self->{path}; 675 return unless $self->{path};
441 676
442 $self->{board_label}->set_text ("Move " . ($self->{showmove} - 1)); 677 $self->{board_label}->set_text ("Move " . ($self->{showmove} - 1));
443 678
444 $self->{cur_board} = new KGS::Game::Board $self->{size}; 679 $self->{cur_board} = new KGS::Game::Board $self->{size};
445 $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $self->{showmove} - 1]]); 680 $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $self->{showmove} - 1]]);
446 681
447 $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_]) 682 $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_])
448 for COLOUR_WHITE, COLOUR_BLACK; 683 for COLOUR_WHITE, COLOUR_BLACK;
449 684
450 if ($self->{rules}{ruleset} == RULESET_JAPANESE) { 685 $self->{board}->set_board ($self->{cur_board});
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} = $self->{handicap} ? COLOUR_WHITE : COLOUR_BLACK;
462 } else {
463 $self->{whosemove} = 1 - $self->{cur_board}{last};
464 }
465 }
466 686
467 my $start_time = $self->{rules}{time}; 687 if ($self->{cur_board}{score}) {
688 $self->{score_inlay} ||= $self->{chat}->new_inlay;
689 $self->{score_inlay}->clear;
690 $self->{score_inlay}->append_text ("\n<header>Scoring</header>"
691 . "\n<score>"
692 . "White: $self->{cur_board}{score}[COLOUR_WHITE], "
693 . "Black: $self->{cur_board}{score}[COLOUR_BLACK]"
694 . "</score>");
695 } elsif ($self->{score_inlay}) {
696 (delete $self->{score_inlay})->clear;
697 }
698
699 $self->update_cursor;
468 700
469 if ($self->{showmove} == @{$self->{path}}) { 701 if ($self->{showmove} == @{$self->{path}}) {
470 $self->{timers} = [ 702 $self->{timers} = [
471 [$self->{lastmove_time}, @{$self->{cur_board}{timer}[0]}], 703 [$self->{lastmove_time}, @{$self->{cur_board}{timer}[0]}],
472 [$self->{lastmove_time}, @{$self->{cur_board}{timer}[1]}], 704 [$self->{lastmove_time}, @{$self->{cur_board}{timer}[1]}],
477 [0, @{$self->{cur_board}{timer}[0]}], 709 [0, @{$self->{cur_board}{timer}[0]}],
478 [0, @{$self->{cur_board}{timer}[1]}], 710 [0, @{$self->{cur_board}{timer}[1]}],
479 ]); 711 ]);
480 } 712 }
481 713
482 $self->{board}->set_board ($self->{cur_board});
483
484 if ($self->{cur_board}{score}) {
485 $self->{score_inlay} ||= $self->{chat}->new_inlay;
486 $self->{score_inlay}->clear;
487 $self->{score_inlay}->append_text ("\n<header>Scoring</header>"
488 . "\n<score>"
489 . "White: $self->{cur_board}{score}[COLOUR_WHITE], "
490 . "Black: $self->{cur_board}{score}[COLOUR_BLACK]"
491 . "</score>");
492 } elsif ($self->{score_inlay}) {
493 (delete $self->{score_inlay})->clear;
494 }
495
496 $self->update_cursor;
497} 714}
498 715
499sub event_update_tree { 716sub event_update_tree {
500 my ($self) = @_; 717 my ($self) = @_;
501 718
519 } 736 }
520} 737}
521 738
522sub event_update_comments { 739sub event_update_comments {
523 my ($self, $node, $comment, $newnode) = @_; 740 my ($self, $node, $comment, $newnode) = @_;
524 $self->SUPER::event_update_comments($node, $comment, $newnode); 741 $self->SUPER::event_update_comments ($node, $comment, $newnode);
525 742
526 my $text; 743 my $text;
527 744
528 $text .= "\n<header>Move <move>$node->{move}</move>, Node <node>$node->{id}</node></header>" 745 $text .= "\n<header>Move <move>$node->{move}</move>, Node <node>$node->{id}</node></header>"
529 if $newnode; 746 if $newnode;
554 } 771 }
555 772
556 $self->{chat}->append_text ($text); 773 $self->{chat}->append_text ($text);
557} 774}
558 775
559sub event_join {
560 my ($self) = @_;
561
562 $self->SUPER::event_join (@_);
563 $self->init_tree;
564 $self->event_update_game;
565}
566
567sub event_part {
568 my ($self) = @_;
569
570 $self->SUPER::event_part;
571 $self->destroy;
572}
573
574sub event_move { 776sub event_move {
575 my ($self, $pass) = @_; 777 my ($self, $pass) = @_;
576 778
577 sound::play 1, $pass ? "pass" : "move"; 779 sound::play 1, $pass ? "pass" : "move";
578} 780}
579 781
580sub event_update_game { 782### GAMEPLAY EVENTS #########################################################
581 my ($self) = @_;
582
583 $self->SUPER::event_update_game;
584
585 return unless $self->{joined};
586
587 $self->{colour} = $self->player_colour ($self->{conn}{name});
588
589 my $title = defined $self->{channel}
590 ? $self->owner->as_string . " " . $self->opponent_string
591 : "Game Window";
592 $self->set_title ("KGS Game $title");
593 $self->{title}->set_text ($title);
594
595 $self->{user}[COLOUR_BLACK] = $self->{black};
596 $self->{user}[COLOUR_WHITE] = $self->{white};
597
598 # show board
599 if ($self->is_inprogress) {
600 if (!$self->{boardbox}->parent) {
601 $self->{boardbox}->add ($self->{board} = new Gtk2::GoBoard size => $self->{size});
602 $self->{left}->add ($self->{boardbox});
603 $self->{board}->signal_connect (button_release => sub {
604 if ($_[1] == 1) {
605 $self->{board_click}->($_[2], $_[3]) if $self->{board_click};
606 }
607 });
608 }
609 if (my $ch = delete $self->{challenge}) {
610 $_->{inlay}->destroy for values %$ch;
611 }
612 $self->update_cursor;
613 }
614
615 $self->{left}->show_all;
616
617 $self->{rules_inlay}->refresh;
618
619}
620
621sub draw_setup {
622 my ($self, $inlay) = @_;
623
624 return unless $self->{joined};
625
626 my $rules = $self->{rules};
627
628 my $text = "";
629
630 $text .= "\nTeacher: <user>" . (util::toxml $self->{teacher}) . "</user>"
631 if $self->{teacher};
632
633 $text .= "\nOwner: <user>" . (util::toxml $self->{owner}->as_string) . "</user>"
634 if $self->{owner}->is_valid;
635
636 if ($self->is_inprogress) {
637 $text .= "\nPlayers: <user>" . (util::toxml $self->{white}->as_string) . "</user>"
638 . " vs. <user>" . (util::toxml $self->{black}->as_string) . "</user>";
639 }
640 $text .= "\nType: " . util::toxml $gametype{$self->type};
641
642 $text .= "\nRuleset: " . $ruleset{$rules->{ruleset}};
643
644 $text .= "\nTime: ";
645
646 if ($rules->{timesys} == TIMESYS_NONE) {
647 $text .= "UNLIMITED";
648 } elsif ($rules->{timesys} == TIMESYS_ABSOLUTE) {
649 $text .= util::format_time $rules->{time};
650 $text .= " ABS";
651 } elsif ($rules->{timesys} == TIMESYS_BYO_YOMI) {
652 $text .= util::format_time $rules->{time};
653 $text .= sprintf " + %s (%d) BY", util::format_time $rules->{interval}, $rules->{count};
654 } elsif ($rules->{timesys} == TIMESYS_CANADIAN) {
655 $text .= util::format_time $rules->{time};
656 $text .= sprintf " + %s/%d CAN", util::format_time $rules->{interval}, $rules->{count};
657 }
658
659 $text .= "\nFlags:";
660 $text .= " private" if $self->is_private;
661 $text .= " started" if $self->is_inprogress;
662 $text .= " adjourned" if $self->is_adjourned;
663 $text .= " scored" if $self->is_scored;
664 $text .= " saved" if $self->is_saved;
665
666 if ($self->is_inprogress) {
667 $text .= "\nHandicap: " . $self->{handicap};
668 $text .= "\nKomi: " . $self->{komi};
669 $text .= "\nSize: " . $self->size_string;
670 }
671
672 if ($self->is_scored) {
673 $text .= "\nResult: " . $self->score_string;
674 }
675
676 $inlay->append_text ("<infoblock>$text</infoblock>");
677
678}
679
680sub event_update_rules {
681 my ($self, $rules) = @_;
682
683 $self->{rules} = $rules;
684
685 if ($self->{user}) {
686 $self->{userpanel}[$_]->configure ($self->{app}, $self->{user}[$_], $rules)
687 for COLOUR_BLACK, COLOUR_WHITE;
688 }
689
690 sound::play 3, "gamestart";
691 $self->{rules_inlay}->refresh;
692}
693 783
694sub event_resign_game { 784sub event_resign_game {
695 my ($self, $player) = @_; 785 my ($self, $player) = @_;
696 786
697 sound::play 3, "resign"; 787 sound::play 3, "resign";
698 $self->{chat}->append_text ("\n<infoblock><header>Resign</header>" 788 $self->{chat}->append_text ("\n<infoblock><header>Resign</header>"
699 . "\n<user>" 789 . "\n<user>"
790 . (util::toxml $self->{user}[1 - $msg->{player}]->as_string)
791 . "</user> resigned."
792 . "\n<user>"
700 . (util::toxml $self->{user}[$msg->{player}]->as_string) 793 . (util::toxml $self->{user}[$msg->{player}]->as_string)
794 . "</user> wins the game."
701 . "</user> resigned.</infoblock>"); 795 . "</infoblock>");
702} 796}
703 797
704sub event_out_of_time { 798sub event_time_win {
705 my ($self, $player) = @_; 799 my ($self, $player) = @_;
706 800
707 sound::play 3, "timewin"; 801 sound::play 3, "timewin";
708 $self->{chat}->append_text ("\n<infoblock><header>Out of Time</header>" 802 $self->{chat}->append_text ("\n<infoblock><header>Out of Time</header>"
709 . "\n<user>" 803 . "\n<user>"
804 . (util::toxml $self->{user}[1 - $msg->{player}]->as_string)
805 . "</user> ran out of time and lost."
806 . "\n<user>"
710 . (util::toxml $self->{user}[$msg->{player}]->as_string) 807 . (util::toxml $self->{user}[$msg->{player}]->as_string)
808 . "</user> wins the game."
809 . "</infoblock>");
810}
811
812sub event_owner_left {
813 my ($self) = @_;
814
815 $self->{chat}->append_text ("\n<infoblock><header>Owner left</header>"
711 . "</user> ran out of time and lost.</infoblock>"); 816 . "\nThe owner of this game left.</infoblock>");
817}
818
819sub event_teacher_left {
820 my ($self) = @_;
821
822 $self->{chat}->append_text ("\n<infoblock><header>Teacher left</header>"
823 . "\nThe teacher left the game.</infoblock>");
712} 824}
713 825
714sub event_done { 826sub event_done {
715 my ($self) = @_; 827 my ($self) = @_;
716 828
736 $self->{chat}->append_text ("<infoblock>\n<header>Game Over</header>" 848 $self->{chat}->append_text ("<infoblock>\n<header>Game Over</header>"
737 . "\nWhite Score " . (util::toxml $msg->{whitescore}->as_string) 849 . "\nWhite Score " . (util::toxml $msg->{whitescore}->as_string)
738 . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string) 850 . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string)
739 . "</infoblock>" 851 . "</infoblock>"
740 ); 852 );
741}
742
743sub inject_set_gametime {
744 my ($self, $msg) = @_;
745
746 $self->{timers} = [
747 [$msg->{NOW}, $msg->{black_time}, $msg->{black_moves}],
748 [$msg->{NOW}, $msg->{white_time}, $msg->{white_moves}],
749 ];
750
751 $self->update_timers ($self->{timers})
752 if $self->{showmove} == @{$self->{path}};
753} 853}
754 854
755sub inject_req_undo { 855sub inject_req_undo {
756 my ($self, $msg) = @_; 856 my ($self, $msg) = @_;
757 857
786 886
787 $self->{chat}->append_text ("\n<header>Game successfully created on server.</header>"); 887 $self->{chat}->append_text ("\n<header>Game successfully created on server.</header>");
788 delete $self->{cid}; 888 delete $self->{cid};
789} 889}
790 890
891### CHALLENGE HANDLING ######################################################
892
791sub draw_challenge { 893sub draw_challenge {
792 my ($self, $id) = @_; 894 my ($self, $id) = @_;
793 895
794 my $info = $self->{challenge}{$id}; 896 my $info = $self->{challenge}{$id};
795 my $inlay = $info->{inlay}; 897 my $inlay = $info->{inlay};
800 902
801 my ($size, $time, $interval, $count, $type); 903 my ($size, $time, $interval, $count, $type);
802 904
803 if (!$self->{channel}) { 905 if (!$self->{channel}) {
804 $inlay->append_text ("\nNotes: "); 906 $inlay->append_text ("\nNotes: ");
805 $inlay->append_entry (\$info->{notes}, 20, ""); 907 $inlay->append_widget (gtk::textentry \$info->{notes}, 20, "");
806 $inlay->append_text ("\nGlobal Offer: "); 908 $inlay->append_text ("\nGlobal Offer: ");
807 $inlay->append_optionmenu (\$info->{flags}, 909 $inlay->append_optionmenu (\$info->{flags},
808 0 => "No", 910 0 => "No",
809 2 => "Yes", 911 2 => "Yes",
810 ); 912 );
863 if ($self->{channel}) { 965 if ($self->{channel}) {
864 $inlay->append_text ("\nHandicap: "); 966 $inlay->append_text ("\nHandicap: ");
865 $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9); 967 $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9);
866 968
867 $inlay->append_text ("\nKomi: "); 969 $inlay->append_text ("\nKomi: ");
868 $inlay->append_entry (\$info->{rules}{komi}, 5); 970 $inlay->append_widget (gtk::numentry \$info->{rules}{komi}, 5);
869 } 971 }
870 972
871 $inlay->append_text ("\nTimesys: "); 973 $inlay->append_text ("\nTimesys: ");
872 $inlay->append_optionmenu ( 974 $inlay->append_optionmenu (
873 \$info->{rules}{timesys}, 975 \$info->{rules}{timesys},
902 } 1004 }
903 } 1005 }
904 ); 1006 );
905 1007
906 $inlay->append_text ("\nMain Time: "); 1008 $inlay->append_text ("\nMain Time: ");
907 $time = $inlay->append_entry (\$info->{rules}{time}, 5); 1009 $time = $inlay->append_widget (gtk::timeentry \$info->{rules}{time}, 5);
908 $inlay->append_text ("\nInterval: "); 1010 $inlay->append_text ("\nInterval: ");
909 $interval = $inlay->append_entry (\$info->{rules}{interval}, 3); 1011 $interval = $inlay->append_widget (gtk::timeentry \$info->{rules}{interval}, 5);
910 $inlay->append_text ("\nPeriods/Stones: "); 1012 $inlay->append_text ("\nPeriods/Stones: ");
911 $count = $inlay->append_entry (\$info->{rules}{count}, 2); 1013 $count = $inlay->append_widget (gtk::numentry \$info->{rules}{count}, 5);
912 1014
913 $inlay->append_text ("\n"); 1015 $inlay->append_text ("\n");
914 1016
915 if (!$self->{channel}) { 1017 if (!$self->{channel}) {
916 $inlay->append_button ("Create Challenge", sub { 1018 $inlay->append_button ("Create Challenge", sub {
950 }); 1052 });
951 } 1053 }
952 } 1054 }
953} 1055}
954 1056
955sub draw_users { 1057sub new_game_challenge {
956 my ($self, $inlay) = @_; 1058 my ($self) = @_;
957 1059
958 for (sort keys %{$self->{users}}) { 1060 my $d = $self->{app}{defaults};
959 $inlay->append_text (" <user>" . $self->{users}{$_}->as_string . "</user>"); 1061
1062 $self->{challenge}{""} = {
1063 gametype => $d->{gametype},
1064 flags => 0,
1065 notes => $d->{stones},
1066 rules => {
1067 ruleset => $d->{ruleset},
1068 size => $d->{size},
1069 timesys => $d->{timesys},
1070 time => $d->{time},
1071 interval => $d->{timesys} == TIMESYS_BYO_YOMI ? $d->{byo_time} : $d->{can_time},
1072 count => $d->{timesys} == TIMESYS_BYO_YOMI ? $d->{byo_periods} : $d->{can_stones},
1073 },
1074
1075 inlay => $self->{chat}->new_inlay,
960 } 1076 };
1077 $self->draw_challenge ("");
961} 1078}
962 1079
963sub event_challenge { 1080sub event_challenge {
964 my ($self, $info) = @_; 1081 my ($self, $info) = @_;
965 1082

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines