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.102 by pcg, Sun May 30 06:40:21 2004 UTC vs.
Revision 1.134 by root, Sun Jun 6 08:01:09 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.
21 23
22 $self->{set} = sub { }; 24 $self->{set} = sub { };
23 $self->{format} = sub { "???" }; 25 $self->{format} = sub { "???" };
24} 26}
25 27
28sub FINALIZE_INSTANCE {
29 my $self = shift;
30
31 $self->stop;
32}
33
26sub configure { 34sub configure {
27 my ($self, $timesys, $main, $interval, $count) = @_; 35 my ($self, $timesys, $main, $interval, $count) = @_;
28 36
29 if ($timesys == TIMESYS_ABSOLUTE) { 37 if ($timesys == TIMESYS_ABSOLUTE) {
30 $self->{set} = sub { $self->{time} = $_[0] }; 38 $self->{format} = sub {
31 $self->{format} = sub { util::format_time $_[0] }; 39 if ($_[0] < 0) {
40 "TIMEOUT";
41 } else {
42 util::format_time $_[0];
43 }
44 };
32 45
33 } elsif ($timesys == TIMESYS_BYO_YOMI) { 46 } elsif ($timesys == TIMESYS_BYO_YOMI) {
34 my $low = $interval * $count; 47 my $low = $interval * $count;
35 48
36 $self->{set} = sub { $self->{time} = $_[0] };
37
38 $self->{format} = sub { 49 $self->{format} = sub {
50 if ($_[0] < 0) {
51 "TIMEOUT";
39 if ($_[0] > $low) { 52 } elsif ($_[0] > $low) {
40 util::format_time $_[0] - $low; 53 util::format_time $_[0] - $low;
41 } else { 54 } else {
42 sprintf "%s (%d)", 55 sprintf "%s (%d)",
43 util::format_time int (($_[0] - 1) % $interval + 1), 56 util::format_time int (($_[0] - 1) % $interval + 1),
44 ($_[0] - 1) / $interval; 57 ($_[0] - 1) / $interval;
45 } 58 }
46 }; 59 };
47 60
48 } elsif ($timesys == TIMESYS_CANADIAN) { 61 } elsif ($timesys == TIMESYS_CANADIAN) {
49 $self->{set} = sub { $self->{time} = $_[0]; $self->{moves} = $_[1] };
50
51 $self->{format} = sub { 62 $self->{format} = sub {
63 if ($_[0] < 0) {
64 "TIMEOUT";
52 if (!$self->{moves}) { 65 } elsif (!$self->{moves}) {
53 util::format_time $_[0] - $low; 66 util::format_time $_[0] - $low;
54 } else { 67 } else {
55 my $time = int (($_[0] - 1) % $interval + 1); 68 my $time = int (($_[0] - 1) % $interval + 1);
56 69
57 sprintf "%s/%d =%d", 70 sprintf "%s/%d =%d",
63 } 76 }
64 }; 77 };
65 78
66 } else { 79 } else {
67 # none, or unknown 80 # none, or unknown
68 $self->{set} = sub { };
69 $self->{format} = sub { "---" } 81 $self->{format} = sub { "-" }
70 } 82 }
71} 83}
72 84
73sub refresh { 85sub refresh {
74 my ($self, $timestamp) = @_; 86 my ($self, $timestamp) = @_;
76 88
77 # we round the timer value slightly... the protocol isn't exact anyways, 89 # we round the timer value slightly... the protocol isn't exact anyways,
78 # and this gives smoother timers ;) 90 # and this gives smoother timers ;)
79 my $timer2 = int $timer + 0.4; 91 my $timer2 = int $timer + 0.4;
80 92
81 if ($timer2 <= 0) { 93 $self->set_text ($self->{format}->($timer2));
82 $timer2 = 0 if $timer2 < 0; 94
83 $self->set_text ("TIME OVER"); 95 $timer - int $timer;
96}
97
98sub set_time {
99 my ($self, $start, $time, $moves) = @_;
100
101 $self->{time} = $time;
102 $self->{moves} = $moves;
103
104 if ($start) {
105 $self->{start} = $start;
106 $self->start;
84 } else { 107 } else {
85 $self->set_text ($self->{format}->($timer2)); 108 $self->stop;
86 }
87
88 $timer - int $timer;
89}
90
91sub set_time {
92 my ($self, $time, $moves) = @_;
93
94 # we ignore requests to re-set the time of a running clock.
95 # this is the easiest way to ensure that commentary etc.
96 # doesn't re-set the clock. yes, this is frickle design,
97 # but I think the protocol is to blame here, which gives
98 # very little time information. (cgoban2 also has had quite
99 # a lot of small time update problems...)
100 unless ($self->{timeout}) {
101 $self->{set}->($time, $moves);
102 $self->refresh ($self->{start}); 109 $self->refresh ($self->{start});
103 } 110 }
104} 111}
105 112
106sub start { 113sub start {
107 my ($self, $when) = @_; 114 my ($self) = @_;
108 115
109 $self->stop; 116 $self->stop;
110
111 $self->{start} = $when;
112 117
113 my $timeout; $timeout = sub { 118 my $timeout; $timeout = sub {
114 my $next = $self->refresh (Time::HiRes::time) * 1000; 119 my $next = $self->refresh (Time::HiRes::time) * 1000;
115 $next += 1000 if $next < 0; 120 $next += 1000 if $next < 0;
116 $self->{timeout} = add Glib::Timeout $next, $timeout; 121 $self->{timeout} = add Glib::Timeout $next, $timeout;
124 my ($self) = @_; 129 my ($self) = @_;
125 130
126 remove Glib::Source delete $self->{timeout} if $self->{timeout}; 131 remove Glib::Source delete $self->{timeout} if $self->{timeout};
127} 132}
128 133
134### USER PANEL ##############################################################
135
129package game::userpanel; 136package game::userpanel;
130 137
138use KGS::Constants;
139
131use Glib::Object::Subclass 140use Glib::Object::Subclass
132 Gtk2::HBox, 141 Gtk2::Frame,
133 properties => [ 142 properties => [
134 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)]),
135 ]; 146 ];
136 147
137sub INIT_INSTANCE { 148sub INIT_INSTANCE {
138 my ($self) = @_; 149 my ($self) = @_;
139 150
151 $self->add ($self->{window} = my $window = new Gtk2::EventBox); # for bg
152
140 $self->add (my $vbox = new Gtk2::VBox); 153 $window->add (my $vbox = new Gtk2::VBox);
141 154
142 $vbox->add ($self->{name} = new Gtk2::Label $self->{name}); 155 $vbox->pack_start (($self->{name} = new Gtk2::Label "-"), 1, 1, 0);
143 $vbox->add ($self->{info} = new Gtk2::Label ""); 156 $vbox->pack_start (($self->{info} = new Gtk2::Label "-"), 1, 1, 0);
144 $vbox->add ($self->{clock} = new game::goclock); Scalar::Util::weaken $self->{clock}; 157 $vbox->pack_start (($self->{clock} = new game::goclock), 1, 1, 0);
145 158
146 $vbox->add ($self->{imagebox} = new Gtk2::VBox); 159 $vbox->add ($self->{imagebox} = new Gtk2::VBox);
147 160
148 $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}");
149} 170}
150 171
151sub configure { 172sub configure {
152 my ($self, $app, $user, $rules) = @_; 173 my ($self, $app, $user, $rules) = @_;
153 174
154 if ($self->{name}->get_text ne $user->as_string) { 175 if ($self->{name}->get_text ne $user->as_string) {
155 $self->{name}->set_text ($user->as_string); 176 $self->{name}->set_text ($user->as_string);
156 177
157 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children; 178 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children;
179 unless ($::config{suppress_userpic}) {
158 $self->{imagebox}->add (gtk::image_from_data undef); 180 $self->{imagebox}->add (gtk::image_from_data undef);
181 }
159 $self->{imagebox}->show_all; 182 $self->{imagebox}->show_all;
160 183
161 if ($user->has_pic) { 184 if ($user->has_pic) {
162 # the big picture... 185 # the big picture...
163 $app->userpic ($user->{name}, sub { 186 $app->userpic ($user->{name}, sub {
164 return unless $self->{imagebox}; 187 return unless $self->{imagebox};
165 188
166 if ($_[0]) { 189 if ($_[0]) {
167 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children; 190 $self->{imagebox}->remove ($_) for $self->{imagebox}->get_children;
191 unless ($::config{suppress_userpic}) {
168 $self->{imagebox}->add (gtk::image_from_data $_[0]); 192 $self->{imagebox}->add (gtk::image_from_data $_[0]);
193 }
169 $self->{imagebox}->show_all; 194 $self->{imagebox}->show_all;
170 } 195 }
171 }); 196 });
172 } 197 }
173 } 198 }
180 205
181 $self->{info}->set_text ("$captures pris."); 206 $self->{info}->set_text ("$captures pris.");
182} 207}
183 208
184sub set_timer { 209sub set_timer {
185 my ($self, $when, $time, $moves) = @_; 210 my ($self, $start, $time, $moves) = @_;
186 211
187 $self->{clock}->stop unless $when;
188 $self->{clock}->set_time ($time, $moves); 212 $self->{clock}->set_time ($start, $time, $moves);
189 $self->{clock}->start ($when) if $when;
190} 213}
214
215### GAME WINDOW #############################################################
191 216
192package game; 217package game;
193 218
194use Scalar::Util qw(weaken); 219use Scalar::Util qw(weaken);
195 220
207 232
208use POSIX qw(ceil); 233use POSIX qw(ceil);
209 234
210sub new { 235sub new {
211 my ($self, %arg) = @_; 236 my ($self, %arg) = @_;
237
212 $self = $self->Glib::Object::new; 238 $self = $self->Glib::Object::new;
213 $self->{$_} = delete $arg{$_} for keys %arg; 239 $self->{$_} = delete $arg{$_} for keys %arg;
214 240
215 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);
216 243
217 $self->signal_connect (destroy => sub { 244 $self->signal_connect (destroy => sub {
218 $self->unlisten; 245 $self->unlisten;
219 delete $self->{app}{game}{$self->{channel}}; 246 delete $self->{app}{game}{$self->{channel}};
220 %{$_[0]} = (); 247 %{$_[0]} = ();
221 });#d# 248 });#d#
222 249
223 $self->add (my $hpane = new Gtk2::HPaned); 250 $self->add (my $hpane = new Gtk2::HPaned);
224 gtk::state $hpane, "game::hpane", undef, position => 500; 251 gtk::state $hpane, "game::hpane", undef, position => 420;
225 252
226 # LEFT PANE 253 # LEFT PANE
227 254
228 $hpane->pack1 (($self->{left} = new Gtk2::VBox), 1, 0); 255 $hpane->pack1 (($self->{left} = new Gtk2::VBox), 1, 0);
229 256
230 $self->{boardbox} = new Gtk2::VBox;
231
232 $hpane->pack1((my $vbox = new Gtk2::VBox), 1, 1); 257 $hpane->pack1((my $vbox = new Gtk2::VBox), 1, 1);
233 258
234 # board box (aspect/canvas) 259 # board box (aspect/canvas)
235 260
236 #$self->{boardbox}->pack_start((my $frame = new Gtk2::Frame), 0, 1, 0);
237
238 # RIGHT PANE 261 # RIGHT PANE
239 262
240 $hpane->pack2 ((my $vbox = new Gtk2::VBox), 1, 1); 263 $hpane->pack2 ((my $vbox = new Gtk2::VBox), 1, 1);
241 $hpane->set (position_set => 1); 264 $hpane->set (position_set => 1);
242 265
243 $vbox->pack_start ((my $frame = new Gtk2::Frame), 0, 1, 0); 266 $vbox->pack_start ((my $frame = new Gtk2::Frame), 0, 1, 0);
244 267
245 { 268 {
246 $frame->add (my $vbox = new Gtk2::VBox); 269 $frame->add (my $vbox = new Gtk2::VBox);
247 $vbox->add ($self->{title} = new Gtk2::Label $title); 270 $vbox->add ($self->{title} = new Gtk2::Label "-");
248 271
249 $vbox->add (my $hbox = new Gtk2::HBox); 272 $vbox->add (my $hbox = new Gtk2::HBox);
250 273
251 $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);
252 275
253 $self->{moveadj} = new Gtk2::Adjustment 1, 1, 1, 1, 5, 0; 276 $self->{moveadj} = new Gtk2::Adjustment 1, 1, 1, 1, 5, 0;
254 277
255 $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);
256 $scale->set_draw_value (0); 279 $scale->set_draw_value (0);
257 $scale->set_digits (0); 280 $scale->set_digits (0);
258 281
259 $self->{moveadj}->signal_connect (value_changed => sub { $self->update_board }); 282 $self->{moveadj}->signal_connect (value_changed => sub {
283 $self->{showmove} = int $self->{moveadj}->get_value;
284 $self->update_board;
285 });
260 } 286 }
261 287
262 $vbox->pack_start ((my $hbox = new Gtk2::HBox 1), 0, 1, 0); 288 $vbox->pack_start ((my $hbox = new Gtk2::HBox 1), 0, 1, 0);
263 289
264 $hbox->add ($self->{userpanel}[$_] = new game::userpanel colour => $_) 290 $hbox->add ($self->{userpanel}[$_] = new game::userpanel colour => $_)
265 for COLOUR_WHITE, COLOUR_BLACK; 291 for COLOUR_WHITE, COLOUR_BLACK;
266 292
267 $vbox->pack_start ((my $buttonbox = new Gtk2::HButtonBox), 0, 1, 0); 293 $vbox->pack_start ((my $buttonbox = new Gtk2::HButtonBox), 0, 1, 0);
268 294
269 $buttonbox->add ($self->{button_pass} = 295 $buttonbox->add ($self->{button_pass} =
270 Gtk2::Button->Glib::Object::new (label => "Pass", no_show_all => 1, visible => 0)); 296 Gtk2::Button->Glib::Object::new (label => "Pass", visible => 0));
271 $self->{button_pass}->signal_connect (clicked => sub { 297 $self->{button_pass}->signal_connect (clicked => sub {
272 $self->{board_click}->(255, 255) if $self->{board_click}; 298 $self->{board_click}->(255, 255) if $self->{board_click};
273 }); 299 });
300 eval { $self->{button_pass}->set (no_show_all => 1) }; # workaround for gtk+-2.2
274 $buttonbox->add ($self->{button_undo} = 301 $buttonbox->add ($self->{button_undo} =
275 Gtk2::Button->Glib::Object::new (label => "Undo", no_show_all => 1, visible => 0)); 302 Gtk2::Button->Glib::Object::new (label => "Undo", visible => 0));
276 $self->{button_undo}->signal_connect (clicked => sub { 303 $self->{button_undo}->signal_connect (clicked => sub {
277 $self->send (req_undo => channel => $self->{channel}); 304 $self->send (req_undo => channel => $self->{channel});
278 }); 305 });
306 eval { $self->{button_undo}->set (no_show_all => 1) }; # workaround for gtk+-2.2
279 $buttonbox->add ($self->{button_resign} = 307 $buttonbox->add ($self->{button_resign} =
280 Gtk2::Button->Glib::Object::new (label => "Resign", no_show_all => 1, visible => 0)); 308 Gtk2::Button->Glib::Object::new (label => "Resign", visible => 0));
281 $self->{button_resign}->signal_connect (clicked => sub { 309 $self->{button_resign}->signal_connect (clicked => sub {
282 $self->send (resign_game => channel => $self->{channel}, player => $self->{colour}); 310 $self->send (resign_game => channel => $self->{channel}, player => $self->{colour});
283 }); 311 });
312 eval { $self->{button_resign}->set (no_show_all => 1) }; # workaround for gtk+-2.2
284 313
285 $vbox->pack_start (($self->{chat} = new superchat), 1, 1, 0); 314 $vbox->pack_start (($self->{chat} = new chat), 1, 1, 0);
286 315
287 $self->set_channel ($self->{channel}); 316 $self->set_channel ($self->{channel});
288 317
289 $self->show_all; 318 $self->show_all;
290 319
312 } 341 }
313 }); 342 });
314 } 343 }
315} 344}
316 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
317sub event_update_users { 394sub event_update_users {
318 my ($self, $add, $update, $remove) = @_; 395 my ($self, $add, $update, $remove) = @_;
319 396
320# $self->{userlist}->update ($add, $update, $remove); 397# $self->{userlist}->update ($add, $update, $remove);
321 398
328 $important{$self->{black}{name}}++; 405 $important{$self->{black}{name}}++;
329 $important{$self->{white}{name}}++; 406 $important{$self->{white}{name}}++;
330 $important{$self->{owner}{name}}++; 407 $important{$self->{owner}{name}}++;
331 408
332 if (my @users = grep $important{$_->{name}}, @$add) { 409 if (my @users = grep $important{$_->{name}}, @$add) {
333 $self->{chat}->append_text ("\n<header>Joins:</header>"); 410 $self->{chat}->append_text ("\n<leader>Joins:</leader>");
334 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users; 411 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users;
335 } 412 }
336 if (my @users = grep $important{$_->{name}}, @$remove) { 413 if (my @users = grep $important{$_->{name}}, @$remove) {
337 $self->{chat}->append_text ("\n<header>Parts:</header>"); 414 $self->{chat}->append_text ("\n<leader>Parts:</leader>");
338 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users; 415 $self->{chat}->append_text (" <user>" . $_->as_string . "</user>") for @users;
339 } 416 }
340} 417}
341 418
342sub join { 419### GAME INFO ###############################################################
420
421sub draw_setup {
343 my ($self) = @_; 422 my ($self, $inlay) = @_;
423
344 return if $self->{joined}; 424 return unless $self->{joined};
345 425
346 $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}};
347} 574}
348 575
349sub update_cursor { 576sub update_cursor {
350 my ($self) = @_; 577 my ($self) = @_;
351 578
352 my $move = int $self->{moveadj}->get_value; 579 return unless $self->{cur_board};
353 my $cb;
354 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 }
597
355 my $running = $move == @{$self->{path}} && $self->is_active; 598 my $running = $self->{showmove} == @{$self->{path}} && $self->is_active;
356 599
357 delete $self->{board_click}; 600 delete $self->{board_click};
358 601
359 if ($self->{teacher} eq $self->{app}{conn}) { 602 if ($self->{teacher} eq $self->{app}{conn}) {
360 #TODO# # teaching mode not implemented 603 #TODO# # teaching mode not implemented
361 $self->{button_pass}->set (label => "Pass", sensitive => 1, visible => 1); 604 $self->{button_pass}->set (label => "Pass", sensitive => 1);
605 $self->{button_pass}->show;
362 $self->{button_undo}->hide; 606 $self->{button_undo}->hide;
363 $self->{button_resign}->hide; 607 $self->{button_resign}->hide;
364 $self->{board}->set (cursor => undef); 608 $self->{board}->set (cursor => undef);
365 609
366 } elsif ($running && $self->{colour} != COLOUR_NONE) { 610 } elsif ($running && $self->{colour} != COLOUR_NONE) {
368 $self->{button_undo}->show; 612 $self->{button_undo}->show;
369 $self->{button_resign}->show; 613 $self->{button_resign}->show;
370 614
371 if ($self->{cur_board}{score}) { 615 if ($self->{cur_board}{score}) {
372 # during scoring 616 # during scoring
373 $self->{button_pass}->set (label => "Done", sensitive => 1, visible => 1); 617 $self->{button_pass}->set (label => "Done", sensitive => 1);
618 $self->{button_pass}->show;
374 $self->{board}->set (cursor => sub { 619 $self->{board}->set (cursor => sub {
375 $_[0] & (MARK_B | MARK_W) 620 $_[0] & (MARK_B | MARK_W)
376 ? $_[0] ^ MARK_GRAYED 621 ? $_[0] ^ MARK_GRAYED
377 : $_[0]; 622 : $_[0];
378 }); 623 });
388 dead => !($self->{cur_board}{board}[$_[0]][$_[1]] & MARK_GRAYED), 633 dead => !($self->{cur_board}{board}[$_[0]][$_[1]] & MARK_GRAYED),
389 ); 634 );
390 } 635 }
391 }; 636 };
392 637
393 } elsif (1 - $self->{colour} == $self->{cur_board}{last}) { 638 } elsif ($self->{colour} == $self->{whosemove}) {
394 # normal move 639 # normal move
395 $self->{button_pass}->set (label => "Pass", sensitive => 1, visible => 1); 640 $self->{button_pass}->set (label => "Pass", sensitive => 1);
641 $self->{button_pass}->show;
396 $self->{board}->set (cursor => sub { 642 $self->{board}->set (cursor => sub {
397 # if is_valid_move oder so#TODO# 643 $self->{cur_board}
398 $_[0] & (MARK_B | MARK_W) 644 && $self->{cur_board}->is_valid_move ($self->{colour}, $_[1], $_[2],
399 ? $_[0] 645 $self->{rules}{ruleset} == RULESET_NEW_ZEALAND)
400 : $_[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];
401 }); 648 });
402 $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);
403 $self->send (game_move => channel => $self->{channel}, x => $_[0], y => $_[1]); 653 $self->send (game_move => channel => $self->{channel}, x => $_[0], y => $_[1]);
404 $self->{board}->set (cursor => undef); 654 $self->{board}->set (cursor => undef);
405 delete $self->{board_click}; 655 delete $self->{board_click};
406 $self->{button_pass}->sensitive (0); 656 $self->{button_pass}->sensitive (0);
407 }; 657 };
408 } else { 658 } else {
409 $self->{button_pass}->set (label => "Pass", sensitive => 0, visible => 1); 659 $self->{button_pass}->set (label => "Pass", sensitive => 0);
660 $self->{button_pass}->show;
661 $self->{board}->set (cursor => undef);
410 } 662 }
411 } else { 663 } else {
412 $self->{button_undo}->hide; 664 $self->{button_undo}->hide;
413 $self->{button_resign}->hide; 665 $self->{button_resign}->hide;
414 $self->{button_pass}->hide; 666 $self->{button_pass}->hide;
417 } 669 }
418} 670}
419 671
420sub update_board { 672sub update_board {
421 my ($self) = @_; 673 my ($self) = @_;
674
422 return unless $self->{path}; 675 return unless $self->{path};
423 676
424 my $move = int $self->{moveadj}->get_value;
425
426 $self->{board_label}->set_text ("Move " . ($move - 1)); 677 $self->{board_label}->set_text ("Move " . ($self->{showmove} - 1));
427 678
428 $self->{cur_board} = new KGS::Game::Board $self->{size}; 679 $self->{cur_board} = new KGS::Game::Board $self->{size};
429 $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $move - 1]]); 680 $self->{cur_board}->interpret_path ([@{$self->{path}}[0 .. $self->{showmove} - 1]]);
430 681
431 $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_]) 682 $self->{userpanel}[$_]->set_captures ($self->{cur_board}{captures}[$_])
432 for COLOUR_WHITE, COLOUR_BLACK; 683 for COLOUR_WHITE, COLOUR_BLACK;
433 684
434 $self->{board}->set_board ($self->{cur_board}); 685 $self->{board}->set_board ($self->{cur_board});
435 686
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
436 $self->update_cursor; 699 $self->update_cursor;
700
701 if ($self->{showmove} == @{$self->{path}}) {
702 $self->{timers} = [
703 [$self->{lastmove_time}, @{$self->{cur_board}{timer}[0]}],
704 [$self->{lastmove_time}, @{$self->{cur_board}{timer}[1]}],
705 ];
706 $self->update_timers ($self->{timers});
707 } else {
708 $self->update_timers ([
709 [0, @{$self->{cur_board}{timer}[0]}],
710 [0, @{$self->{cur_board}{timer}[1]}],
711 ]);
712 }
713
437} 714}
438 715
439sub event_update_tree { 716sub event_update_tree {
440 my ($self) = @_; 717 my ($self) = @_;
718
719 (delete $self->{undo_inlay})->clear
720 if $self->{undo_inlay};
441 721
442 $self->{path} = $self->get_path; 722 $self->{path} = $self->get_path;
443 723
444 if ($self->{moveadj}) { 724 if ($self->{moveadj}) {
445 my $upper = $self->{moveadj}->upper; 725 my $upper = $self->{moveadj}->upper;
456 } 736 }
457} 737}
458 738
459sub event_update_comments { 739sub event_update_comments {
460 my ($self, $node, $comment, $newnode) = @_; 740 my ($self, $node, $comment, $newnode) = @_;
461 $self->SUPER::event_update_comments($node, $comment, $newnode); 741 $self->SUPER::event_update_comments ($node, $comment, $newnode);
462 742
463 my $text; 743 my $text;
464 744
465 $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>"
466 if $newnode; 746 if $newnode;
491 } 771 }
492 772
493 $self->{chat}->append_text ($text); 773 $self->{chat}->append_text ($text);
494} 774}
495 775
496sub event_join {
497 my ($self) = @_;
498
499 $self->SUPER::event_join (@_);
500 $self->init_tree;
501 $self->event_update_game;
502}
503
504sub event_part {
505 my ($self) = @_;
506
507 $self->SUPER::event_part;
508 $self->destroy;
509}
510
511sub event_move { 776sub event_move {
512 my ($self, $pass) = @_; 777 my ($self, $pass) = @_;
778
513 sound::play 1, $pass ? "pass" : "move"; 779 sound::play 1, $pass ? "pass" : "move";
514
515 if ($self->{undo_inlay}) {
516 (delete $self->{undo_inlay})->clear;
517 }
518} 780}
519 781
520sub event_update_game { 782### GAMEPLAY EVENTS #########################################################
521 my ($self) = @_;
522
523 $self->SUPER::event_update_game;
524
525 return unless $self->{joined};
526
527 $self->{colour} = $self->player_colour ($self->{conn}{name});
528
529 my $title = defined $self->{channel}
530 ? $self->owner->as_string . " " . $self->opponent_string
531 : "Game Window";
532 $self->set_title ("KGS Game $title");
533 $self->{title}->set_text ($title);
534
535 $self->{user}[COLOUR_BLACK] = $self->{black};
536 $self->{user}[COLOUR_WHITE] = $self->{white};
537
538 # show board
539 if ($self->is_inprogress) {
540 if (!$self->{boardbox}->parent) {
541 $self->{boardbox}->add ($self->{board} = new Gtk2::GoBoard size => $self->{size});
542 $self->{left}->add ($self->{boardbox});
543 $self->{board}->signal_connect (button_release => sub {
544 if ($_[1] == 1) {
545 $self->{board_click}->($_[2], $_[3]) if $self->{board_click};
546 }
547 });
548 }
549 if (my $ch = delete $self->{challenge}) {
550 $_->{inlay}->destroy for values %$ch;
551 }
552 $self->update_cursor;
553 }
554
555 $self->{left}->show_all;
556
557 $self->{rules_inlay}->refresh;
558
559}
560
561sub draw_setup {
562 my ($self, $inlay) = @_;
563
564 return unless $self->{joined};
565
566 my $rules = $self->{rules};
567
568 my $text = "";
569
570 $text .= "\nTeacher: <user>" . (util::toxml $self->{teacher}) . "</user>"
571 if $self->{teacher};
572
573 $text .= "\nOwner: <user>" . (util::toxml $self->{owner}->as_string) . "</user>"
574 if $self->{owner}->is_valid;
575
576 if ($self->is_inprogress) {
577 $text .= "\nPlayers: <user>" . (util::toxml $self->{white}->as_string) . "</user>"
578 . " vs. <user>" . (util::toxml $self->{black}->as_string) . "</user>";
579 }
580 $text .= "\nType: " . util::toxml $gametype{$self->type};
581
582 $text .= "\nRuleset: " . $ruleset{$rules->{ruleset}};
583
584 $text .= "\nTime: ";
585
586 if ($rules->{timesys} == TIMESYS_NONE) {
587 $text .= "UNLIMITED";
588 } elsif ($rules->{timesys} == TIMESYS_ABSOLUTE) {
589 $text .= util::format_time $rules->{time};
590 $text .= " ABS";
591 } elsif ($rules->{timesys} == TIMESYS_BYO_YOMI) {
592 $text .= util::format_time $rules->{time};
593 $text .= sprintf " + %s (%d) BY", util::format_time $rules->{interval}, $rules->{count};
594 } elsif ($rules->{timesys} == TIMESYS_CANADIAN) {
595 $text .= util::format_time $rules->{time};
596 $text .= sprintf " + %s/%d CAN", util::format_time $rules->{interval}, $rules->{count};
597 }
598
599 $text .= "\nFlags:";
600 $text .= " private" if $self->is_private;
601 $text .= " started" if $self->is_inprogress;
602 $text .= " adjourned" if $self->is_adjourned;
603 $text .= " scored" if $self->is_scored;
604 $text .= " saved" if $self->is_saved;
605
606 if ($self->is_inprogress) {
607 $text .= "\nHandicap: " . $self->{handicap};
608 $text .= "\nKomi: " . $self->{komi};
609 $text .= "\nSize: " . $self->size_string;
610 }
611
612 if ($self->is_scored) {
613 $text .= "\nResult: " . $self->score_string;
614 }
615
616 $inlay->append_text ("<infoblock>$text</infoblock>");
617
618}
619
620sub event_update_rules {
621 my ($self, $rules) = @_;
622
623 $self->{rules} = $rules;
624
625 if ($self->{user}) {
626 $self->{userpanel}[$_]->configure ($self->{app}, $self->{user}[$_], $rules)
627 for COLOUR_BLACK, COLOUR_WHITE;
628 }
629
630 sound::play 3, "gamestart";
631
632 $self->{rules_inlay}->refresh;
633}
634 783
635sub event_resign_game { 784sub event_resign_game {
636 my ($self, $player) = @_; 785 my ($self, $player) = @_;
637 786
638 sound::play 3, "resign"; 787 sound::play 3, "resign";
639 $self->{chat}->append_text ("\n<infoblock><header>Resign</header>" 788 $self->{chat}->append_text ("\n<infoblock><header>Resign</header>"
640 . "\n<user>" 789 . "\n<user>"
641 . (util::toxml $self->{user}[$msg->{player}]->as_string) 790 . (util::toxml $self->{user}[$player]->as_string)
642 . "</user> resigned.</infoblock>"); 791 . "</user> resigned."
792 . "\n<user>"
793 . (util::toxml $self->{user}[1 - $player]->as_string)
794 . "</user> wins the game."
795 . "</infoblock>");
643} 796}
644 797
645sub event_out_of_time { 798sub event_out_of_time {
646 my ($self, $player) = @_; 799 my ($self, $player) = @_;
647 800
648 sound::play 3, "timewin"; 801 sound::play 3, "timewin";
649 $self->{chat}->append_text ("\n<infoblock><header>Time Over</header>" 802 $self->{chat}->append_text ("\n<infoblock><header>Out of Time</header>"
650 . "\n<user>" 803 . "\n<user>"
651 . (util::toxml $self->{user}[$msg->{player}]->as_string) 804 . (util::toxml $self->{user}[$msg->{player}]->as_string)
652 . "</user> ran out of time.</infoblock>"); 805 . "</user> ran out of time and lost."
806 . "\n<user>"
807 . (util::toxml $self->{user}[1 - $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>"
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>");
653} 824}
654 825
655sub event_done { 826sub event_done {
656 my ($self) = @_; 827 my ($self) = @_;
657 828
658 if ($self->{done}[1 - $self->{colour}] && !$self->{done}[$self->{colour}]) { 829 if ($self->{done}[1 - $self->{colour}] && !$self->{done}[$self->{colour}]) {
830 sound::play 2, "info" unless $inlay->{count};
659 $self->{chat}->append_text ("\n<infoblock><header>Done</header>" 831 $self->{chat}->append_text ("\n<infoblock><header>Press Done</header>"
660 . "\nYour opponent pressed done."); 832 . "\nYour opponent pressed done. Now it's up to you.");
661 } 833 }
834 if ($self->{doneid} & 0x80000000) {
835 sound::play 2, "info" unless $inlay->{count};
836 $self->{chat}->append_text ("\n<infoblock><header>Press Done Again</header>"
837 . "\nThe board has changed.");
838 }
839
840 $self->{button_pass}->sensitive (!$self->{done}[$self->{colour}]);
841
842 $self->{chat}->set_end;
662} 843}
663 844
664sub inject_final_result { 845sub inject_final_result {
665 my ($self, $msg) = @_; 846 my ($self, $msg) = @_;
666 847
669 . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string) 850 . "\nBlack Score " . (util::toxml $msg->{blackscore}->as_string)
670 . "</infoblock>" 851 . "</infoblock>"
671 ); 852 );
672} 853}
673 854
674sub 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});
687}
688
689sub inject_req_undo { 855sub inject_req_undo {
690 my ($self, $msg) = @_; 856 my ($self, $msg) = @_;
691 857
692 my $inlay = $self->{undo_inlay} ||= $self->{chat}->new_inlay; 858 my $inlay = $self->{undo_inlay} ||= $self->{chat}->new_inlay;
693 return if $inlay->{ignore}; 859 return if $inlay->{ignore};
694 860
695 sound::play 3, "warning" unless $inlay->{count}; 861 sound::play 2, "warning" unless $inlay->{count};
696 $inlay->{count}++; 862 $inlay->{count}++;
697 863
698 $inlay->clear; 864 $inlay->clear;
699 $inlay->append_text ("\n<undo>Undo requested ($inlay->{count} times)</undo>\n"); 865 $inlay->append_text ("\n<undo>Undo requested ($inlay->{count} times)</undo>\n");
700 $inlay->append_button ("Grant", sub { 866 $inlay->append_button ("Grant", sub {
701 $inlay->clear; 867 (delete $self->{undo_inlay})->clear;
702 $self->send (grant_undo => channel => $self->{channel}); 868 $self->send (grant_undo => channel => $self->{channel});
703 }); 869 });
704 $inlay->append_button ("Ignore", sub { 870 $inlay->append_button ("Ignore", sub {
705 $inlay->clear; 871 $inlay->clear;
706 $inlay->{ignore} = 1; 872 $inlay->{ignore} = 1;
711} 877}
712 878
713sub inject_new_game { 879sub inject_new_game {
714 my ($self, $msg) = @_; 880 my ($self, $msg) = @_;
715 881
716 $self->{chat}->append_text ("\n<header>ACK from server ($msg->{cid} == $self->{cid})</header>"); 882 if ($msg->{cid} != $self->{cid}) {
883 $self->part;
884 warn "ERROR: challenge id mismatch, PLEASE REPORT, especially the circumstances (many games open? etc..)\n";#d#
885 }
886
887 $self->{chat}->append_text ("\n<header>Game successfully created on server.</header>");
717 delete $self->{cid}; 888 delete $self->{cid};
718} 889}
890
891### CHALLENGE HANDLING ######################################################
719 892
720sub draw_challenge { 893sub draw_challenge {
721 my ($self, $id) = @_; 894 my ($self, $id) = @_;
722 895
723 my $info = $self->{challenge}{$id}; 896 my $info = $self->{challenge}{$id};
725 my $rules = $info->{rules}; 898 my $rules = $info->{rules};
726 899
727 my $as_black = $info->{black}{name} eq $self->{conn}{name} ? 1 : 0;; 900 my $as_black = $info->{black}{name} eq $self->{conn}{name} ? 1 : 0;;
728 my $opponent = $as_black ? $info->{white} : $info->{black}; 901 my $opponent = $as_black ? $info->{white} : $info->{black};
729 902
730 my ($size, $time, $interval, $count); 903 my ($size, $time, $interval, $count, $type);
731 904
732 if (!$self->{channel}) { 905 if (!$self->{channel}) {
733 $inlay->append_text ("\nNotes: "); 906 $inlay->append_text ("\nNotes: ");
734 $inlay->append_entry (\$info->{notes}, 20, ""); 907 $inlay->append_widget (gtk::textentry \$info->{notes}, 20, "");
735 $inlay->append_text ("\nGlobal Offer: "); 908 $inlay->append_text ("\nGlobal Offer: ");
736 $inlay->append_optionmenu (\$info->{flags}, 909 $inlay->append_optionmenu (\$info->{flags},
737 0 => "No", 910 0 => "No",
738 2 => "Yes", 911 2 => "Yes",
739 ); 912 );
740 } else { 913 } else {
741 $inlay->append_text ("\nNotes: " . util::toxml $info->{notes}); 914 $inlay->append_text ("\nNotes: " . util::toxml $info->{notes});
742 } 915 }
743 916
744 $inlay->append_text ("\nType: "); 917 $inlay->append_text ("\nType: ");
745 $inlay->append_optionmenu ( 918 $type = $inlay->append_optionmenu (
746 \$info->{gametype}, 919 \$info->{gametype},
747 GAMETYPE_DEMONSTRATION , "Demonstration", 920 GAMETYPE_DEMONSTRATION , "Demonstration (not yet)",
748 GAMETYPE_DEMONSTRATION | GAMETYPE_PRIVATE, "Demonstration (P)", 921 GAMETYPE_DEMONSTRATION | GAMETYPE_PRIVATE, "Demonstration (P) (not yet)",
749 GAMETYPE_TEACHING , "Teaching", 922 GAMETYPE_TEACHING , "Teaching (not yet)",
750 GAMETYPE_TEACHING | GAMETYPE_PRIVATE, "Teaching (P)", 923 GAMETYPE_TEACHING | GAMETYPE_PRIVATE, "Teaching (P) (not yet)",
751 GAMETYPE_SIMUL , "Simul (not yet!)", 924 GAMETYPE_SIMUL , "Simul (not yet!)",
752 GAMETYPE_FREE , "Free", 925 GAMETYPE_FREE , "Free",
753 GAMETYPE_RATED , "Rated", 926 GAMETYPE_RATED , "Rated",
754 sub { 927 sub {
755 $size->set_history (2) if $_[0] eq GAMETYPE_RATED; 928 $size->set_history (2) if $_[0] eq GAMETYPE_RATED;
779 RULESET_NEW_ZEALAND, "New Zealand", 952 RULESET_NEW_ZEALAND, "New Zealand",
780 ); 953 );
781 954
782 $inlay->append_text ("\nSize: "); 955 $inlay->append_text ("\nSize: ");
783 $size = $inlay->append_optionmenu ( 956 $size = $inlay->append_optionmenu (
957 \$info->{rules}{size},
784 \$info->{rules}{size}, 9 => 9, 13 => 13, 19 => 19, map +($_, $_), 2..38 958 (9 => 9, 13 => 13, 19 => 19, map +($_, $_), 2..38),
959 sub {
960 $type->set_history (5) # reset to free
961 if $_[0] != 19 && $info->{gametype} == GAMETYPE_RATED;
962 },
785 ); 963 );
786 964
787 if ($self->{channel}) { 965 if ($self->{channel}) {
788 $inlay->append_text ("\nHandicap: "); 966 $inlay->append_text ("\nHandicap: ");
789 $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9); 967 $inlay->append_optionmenu (\$info->{rules}{handicap}, map +($_, $_), 0..9);
790 968
791 $inlay->append_text ("\nKomi: "); 969 $inlay->append_text ("\nKomi: ");
792 $inlay->append_entry (\$info->{rules}{komi}, 5); 970 $inlay->append_widget (gtk::numentry \$info->{rules}{komi}, 5);
793 } 971 }
794 972
795 $inlay->append_text ("\nTimesys: "); 973 $inlay->append_text ("\nTimesys: ");
796 $inlay->append_optionmenu ( 974 $inlay->append_optionmenu (
797 \$info->{rules}{timesys}, 975 \$info->{rules}{timesys},
826 } 1004 }
827 } 1005 }
828 ); 1006 );
829 1007
830 $inlay->append_text ("\nMain Time: "); 1008 $inlay->append_text ("\nMain Time: ");
831 $time = $inlay->append_entry (\$info->{rules}{time}, 5); 1009 $time = $inlay->append_widget (gtk::timeentry \$info->{rules}{time}, 5);
832 $inlay->append_text ("\nInterval: "); 1010 $inlay->append_text ("\nInterval: ");
833 $interval = $inlay->append_entry (\$info->{rules}{interval}, 3); 1011 $interval = $inlay->append_widget (gtk::timeentry \$info->{rules}{interval}, 5);
834 $inlay->append_text ("\nPeriods/Stones: "); 1012 $inlay->append_text ("\nPeriods/Stones: ");
835 $count = $inlay->append_entry (\$info->{rules}{count}, 2); 1013 $count = $inlay->append_widget (gtk::numentry \$info->{rules}{count}, 5);
836 1014
837 $inlay->append_text ("\n"); 1015 $inlay->append_text ("\n");
838 1016
839 if (!$self->{channel}) { 1017 if (!$self->{channel}) {
840 $inlay->append_button ("Create Challenge", sub { 1018 $inlay->append_button ("Create Challenge", sub {
874 }); 1052 });
875 } 1053 }
876 } 1054 }
877} 1055}
878 1056
879sub draw_users { 1057sub new_game_challenge {
880 my ($self, $inlay) = @_; 1058 my ($self) = @_;
881 1059
882 for (sort keys %{$self->{users}}) { 1060 my $d = $self->{app}{defaults};
883 $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,
884 } 1076 };
1077 $self->draw_challenge ("");
885} 1078}
886 1079
887sub event_challenge { 1080sub event_challenge {
888 my ($self, $info) = @_; 1081 my ($self, $info) = @_;
889 1082
890 my $as_black = $info->{black}->{name} eq $self->{conn}{name}; 1083 my $as_black = $info->{black}->{name} eq $self->{conn}{name};
891 my $opponent = $as_black ? $info->{white} : $info->{black}; 1084 my $opponent = $as_black ? $info->{white} : $info->{black};
892 1085
893 my $id = $opponent->{name}; 1086 my $id = $opponent->{name};
1087
1088 sound::play 2, "info";
894 1089
895 $self->{challenge}{$id} = $info; 1090 $self->{challenge}{$id} = $info;
896 $self->{challenge}{$id}{inlay} = $self->{chat}->new_switchable_inlay ( 1091 $self->{challenge}{$id}{inlay} = $self->{chat}->new_switchable_inlay (
897 exists $self->{challenge}{""} 1092 exists $self->{challenge}{""}
898 ? "Challenge from $opponent->{name}" 1093 ? "Challenge from " . $opponent->as_string
899 : "Challenge to $opponent->{name}", 1094 : "Challenge to " . $opponent->as_string,
900 sub { 1095 sub {
901 $self->{challenge}{$id}{inlay} = $_[0]; 1096 $self->{challenge}{$id}{inlay} = $_[0];
902 $self->draw_challenge ($id); 1097 $self->draw_challenge ($id);
903 }, 1098 },
904 !exists $self->{challenge}{""} # only open when not offerer 1099 !exists $self->{challenge}{""} # only open when not offerer

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines