ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.103
Committed: Mon Jul 24 08:23:28 2006 UTC (17 years, 10 months ago) by root
Branch: MAIN
Changes since 1.102: +11 -108 lines
Log Message:
use yaml for config, many misc fixes

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3     use strict;
4     use utf8;
5    
6     # do things only needed for single-binary version (par)
7     BEGIN {
8     if (%PAR::LibCache) {
9     @INC = grep ref, @INC; # weed out all paths except pars loader refs
10    
11     while (my ($filename, $zip) = each %PAR::LibCache) {
12     for ($zip->memberNames) {
13     next unless /^\/root\/(.*)/;
14     $zip->extractMember ($_, "$ENV{PAR_TEMP}/$1")
15     unless -e "$ENV{PAR_TEMP}/$1";
16     }
17     }
18    
19     # TODO: pango-rc file, anybody?
20    
21     unshift @INC, $ENV{PAR_TEMP};
22     }
23     }
24    
25     # need to do it again because that pile of garbage called PAR nukes it before main
26     unshift @INC, $ENV{PAR_TEMP}
27     if %PAR::LibCache;
28    
29     use Time::HiRes 'time';
30     use Event;
31    
32     use Crossfire;
33 root 1.12 use Crossfire::Protocol::Constants;
34 root 1.1
35     use Compress::LZF;
36    
37     use CFClient;
38 root 1.10 use CFClient::OpenGL ();
39 root 1.11 use CFClient::Protocol;
40 root 1.1 use CFClient::UI;
41 root 1.100 use CFClient::Pod;
42 root 1.80 use CFClient::BindingEditor;
43 root 1.1 use CFClient::MapWidget;
44    
45 root 1.59 $SIG{QUIT} = sub { Carp::cluck "QUIT" };
46 root 1.91 $SIG{PIPE} = 'IGNORE';
47 root 1.59
48 root 1.103 $Event::Eval = 0;
49 root 1.1 $Event::DIED = sub {
50     # TODO: display dialog box or so
51 root 1.103 Carp::cluck $_[1];#d#TODO: remove when stable
52 root 1.1 CFClient::error $_[1];
53     };
54    
55 root 1.103 $SIG{__DIE__} = sub {
56     return if CFClient::in_destruct;
57     Carp::cluck $_[0];
58     CFClient::error $_[0];
59     return;#d#
60     #return unless defined $^S && !$^S;
61     $Event::DIED->(undef, $_[0]);
62     };
63 root 1.1
64     our $VERSION = '0.1';
65    
66     my $MAX_FPS = 60;
67     my $MIN_FPS = 5; # unused as of yet
68    
69     our $META_SERVER = "crossfire.real-time.com:13326";
70    
71     our $LAST_REFRESH;
72     our $NOW;
73    
74     our $CFG;
75     our $CONN;
76     our $FAST; # fast, low-quality mode, possibly useful for software-rendering
77    
78     our $WANT_REFRESH;
79     our $CAN_REFRESH;
80    
81     our @SDL_MODES;
82     our $WIDTH;
83     our $HEIGHT;
84     our $FULLSCREEN;
85     our $FONTSIZE;
86    
87     our $FONT_PROP;
88     our $FONT_FIXED;
89    
90     our $MAP;
91     our $MAPMAP;
92     our $MAPWIDGET;
93     our $BUTTONBAR;
94     our $LOGVIEW;
95     our $CONSOLE;
96     our $METASERVER;
97     our $LOGIN_BUTTON;
98     our $QUIT_DIALOG;
99 root 1.40 our $HOST_ENTRY;
100 root 1.94 our $FULLSCREEN_ENABLE;
101 root 1.86 our $PICKUP_ENABLE;
102 root 1.67 our $SERVER_INFO;
103 root 1.49
104     our $SETUP_DIALOG;
105     our $SETUP_NOTEBOOK;
106     our $SETUP_SERVER;
107     our $SETUP_KEYBOARD;
108 root 1.1
109 root 1.86 our $PL_NOTEBOOK;
110     our $PL_WINDOW;
111    
112     our $INVENTORY_PAGE;
113     our $STATS_PAGE;
114 root 1.95 our $SKILL_PAGE;
115 root 1.86 our $SPELL_PAGE;
116    
117     our $HELP_WINDOW;
118 root 1.60 our $MESSAGE_WINDOW;
119 root 1.1 our $FLOORBOX;
120     our $GAUGES;
121     our $STATWIDS;
122    
123     our $SDL_ACTIVE;
124     our %SDL_CB;
125    
126     our $SDL_MIXER;
127     our @SOUNDS; # event => file mapping
128     our %AUDIO_CHUNKS; # audio files
129    
130     our $ALT_ENTER_MESSAGE;
131     our $STATUSBOX;
132     our $DEBUG_STATUS;
133    
134     our $INV;
135     our $INVR;
136 elmex 1.27 our $INV_RIGHT_HB;
137 root 1.1
138 elmex 1.34 our $BIND_EDITOR;
139 elmex 1.77 our $BIND_UPD_CB;
140 elmex 1.24
141 elmex 1.43 our $PICKUP_CFG;
142 elmex 1.38
143 root 1.1 sub status {
144 root 1.100 $STATUSBOX->add (CFClient::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
145 root 1.1 }
146    
147     sub debug {
148     $DEBUG_STATUS->set_text ($_[0]);
149     }
150    
151 root 1.60 sub destroy_query_dialog {
152     (delete $_[0]{query_dialog})->destroy
153     if $_[0]{query_dialog};
154     }
155    
156     # server query dialog
157     sub server_query {
158     my ($conn, $flags, $prompt) = @_;
159    
160     $conn->{query_dialog} = my $dialog = new CFClient::UI::FancyFrame
161     x => "center",
162     y => "center",
163     title => "Server Query",
164     child => my $vbox = new CFClient::UI::VBox,
165     ;
166    
167     my @dialog = my $label = new CFClient::UI::Label
168     max_w => $::WIDTH * 0.4,
169     ellipsise => 0,
170     text => $prompt;
171    
172     if ($flags & CS_QUERY_YESNO) {
173     push @dialog, my $hbox = new CFClient::UI::HBox;
174    
175     $hbox->add (new CFClient::UI::Button
176     text => "No",
177     on_activate => sub {
178     $conn->send ("reply n");
179     $dialog->destroy;
180 root 1.74 0
181 root 1.60 }
182     );
183     $hbox->add (new CFClient::UI::Button
184     text => "Yes",
185     on_activate => sub {
186     $conn->send ("reply y");
187     destroy_query_dialog $conn;
188 root 1.74 0
189 root 1.60 },
190     );
191    
192 root 1.74 $dialog->grab_focus;
193 root 1.60
194     } elsif ($flags & CS_QUERY_SINGLECHAR) {
195     $dialog->{tooltip} = "Press a key (click on the entry to make sure it has keyboard focus)";
196    
197     if ($prompt =~ /Now choose a character|Press any key for the next race/i) {
198     $MESSAGE_WINDOW->show;
199    
200     unshift @dialog, new CFClient::UI::Label
201     max_w => $::WIDTH * 0.4,
202     ellipsise => 0,
203 root 1.98 markup => "\nOr use your keyboard and the text entry below:\n";
204 root 1.60
205     unshift @dialog, my $table = new CFClient::UI::Table;
206    
207     $table->add (0, 0, new CFClient::UI::Button
208     text => "Next Race",
209     on_activate => sub {
210     $conn->send ("reply n");
211     destroy_query_dialog $conn;
212 root 1.74 0
213 root 1.60 },
214     );
215     $table->add (2, 0, new CFClient::UI::Button
216     text => "Accept",
217     on_activate => sub {
218     $conn->send ("reply d");
219     destroy_query_dialog $conn;
220 root 1.74 0
221 root 1.60 },
222     );
223    
224     unshift @dialog, new CFClient::UI::Label
225     max_w => $::WIDTH * 0.4,
226     ellipsise => 0,
227     markup =>
228     "<big><b>Character Creation: Race</b></big>\n\n"
229     . "Look at the <b>Messages</b> window to see a description of this race "
230     . "and the center of the screen to see how this race looks like "
231 root 1.98 . "(<small>below this dialog window: you may need to move the dialog away and "
232     . "click into the display area to make it visible</small>).\n\n"
233     . "You can look at another race, or accept this race (you will cycle back to "
234     . "this race eventually, so you can take your time making this important choice."
235 root 1.60 ;
236    
237     } elsif ($prompt =~ /roll new stats/) {
238     if (my $stat = delete $conn->{stat_change_with}) {
239     $conn->send ("reply $stat");
240     destroy_query_dialog $conn;
241     return;
242     }
243    
244 root 1.86 $STATS_PAGE->show;
245 root 1.60 $MESSAGE_WINDOW->hide;
246    
247     unshift @dialog, new CFClient::UI::Label
248     max_w => $::WIDTH * 0.4,
249     ellipsise => 0,
250 root 1.98 markup => "\nOr use your keyboard and the text entry below:\n";
251 root 1.60
252     unshift @dialog, my $table = new CFClient::UI::Table;
253    
254     # left: re-roll
255     $table->add (0, 0, new CFClient::UI::Button
256     text => "Roll Again",
257     on_activate => sub {
258     $conn->send ("reply y");
259     destroy_query_dialog $conn;
260 root 1.74 0
261 root 1.60 },
262     );
263    
264     # center: swap stats
265 root 1.102 my ($sw1, $sw2) = map +(new CFClient::UI::Selector
266 root 1.98 expand => 1,
267 root 1.60 value => $_,
268     options => [
269 root 1.64 [1 => "Str", "Strength ($conn->{stat}{+CS_STAT_STR})"],
270     [2 => "Dex", "Dexterity ($conn->{stat}{+CS_STAT_DEX})"],
271     [3 => "Con", "Constitution ($conn->{stat}{+CS_STAT_CON})"],
272     [4 => "Int", "Intelligence ($conn->{stat}{+CS_STAT_INT})"],
273     [5 => "Wis", "Wisdom ($conn->{stat}{+CS_STAT_WIS})"],
274     [6 => "Pow", "Power ($conn->{stat}{+CS_STAT_POW})"],
275     [7 => "Cha", "Charisma ($conn->{stat}{+CS_STAT_CHA})"],
276 root 1.60 ],
277     ), 1 .. 2;
278    
279     $table->add (2, 0, new CFClient::UI::Button
280     text => "Swap Stats",
281     on_activate => sub {
282     $conn->{stat_change_with} = $sw2->{value};
283     $conn->send ("reply $sw1->{value}");
284     destroy_query_dialog $conn;
285 root 1.74 0
286 root 1.60 },
287     );
288     $table->add (2, 1, new CFClient::UI::HBox children => [$sw1, $sw2]);
289    
290     # right: accept
291     $table->add (4, 0, new CFClient::UI::Button
292     text => "Accept",
293     on_activate => sub {
294     $conn->send ("reply n");
295 root 1.86 $STATS_PAGE->hide;
296 root 1.60 destroy_query_dialog $conn;
297 root 1.74 0
298 root 1.60 },
299     );
300    
301 root 1.98 unshift @dialog, my $hbox = new CFClient::UI::HBox;
302     for (
303     [Str => CS_STAT_STR],
304     [Dex => CS_STAT_DEX],
305     [Con => CS_STAT_CON],
306     [Int => CS_STAT_INT],
307     [Wis => CS_STAT_WIS],
308     [Pow => CS_STAT_POW],
309     [Cha => CS_STAT_CHA],
310     ) {
311     my ($name, $id) = @$_;
312     $hbox->add (new CFClient::UI::Label
313     markup => "$conn->{stat}{$id} <span foreground='yellow'>$name</span>",
314     align => 0,
315     expand => 1,
316     can_events => 1,
317     can_hover => 1,
318     tooltip => $CFClient::STAT_TOOLTIP{$name},
319     );
320     }
321    
322 root 1.60 unshift @dialog, new CFClient::UI::Label
323     max_w => $::WIDTH * 0.4,
324     ellipsise => 0,
325     markup =>
326     "<big><b>Character Creation: Stats</b></big>\n\n"
327 root 1.98 . "<b>Stats</b> are a very important aspect of your character. You can use the tooltips to learn what each Stat governs.\n\n"
328     . "The stats generated by the server are always sorted from Str (highest) to Cha (lowest). "
329     . "They will be modified later by both the race and the class you choose.\n\n"
330     . "You can create another set of stats, swap two stat values with each other or accept the stats as shown below and continue.\n"
331 root 1.60 ;
332     }
333    
334     push @dialog, my $entry = new CFClient::UI::Entry
335     on_changed => sub {
336     $conn->send ("reply $_[1]");
337     destroy_query_dialog $conn;
338 root 1.74 0
339 root 1.60 },
340     ;
341    
342 root 1.74 $entry->grab_focus;
343 root 1.60
344     } else {
345     $dialog->{tooltip} = "Enter the reply and press return (click on the entry to make sure it has keyboard focus)";
346    
347     push @dialog, my $entry = new CFClient::UI::Entry
348     $flags & CS_QUERY_HIDEINPUT ? (hidden => "*") : (),
349     on_activate => sub {
350     $conn->send ("reply $_[1]");
351     destroy_query_dialog $conn;
352 root 1.74 0
353 root 1.60 },
354     ;
355    
356 root 1.74 $entry->grab_focus;
357 root 1.60 }
358    
359     $vbox->add (@dialog);
360     $dialog->show;
361     }
362    
363 root 1.1 sub start_game {
364     status "logging in...";
365    
366 root 1.23 $LOGIN_BUTTON->set_text ("Logout");
367 root 1.49 $SETUP_DIALOG->hide;
368 root 1.23
369 root 1.1 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
370    
371 root 1.75 my ($host, $port) = split /:/, $CFG->{profile}{default}{host};
372 root 1.11
373 root 1.1 $MAP = new CFClient::Map $mapsize, $mapsize;
374    
375     $CONN = eval {
376 root 1.11 new CFClient::Protocol
377 root 1.1 host => $host,
378     port => $port || 13327,
379 root 1.75 user => $CFG->{profile}{default}{user},
380     pass => $CFG->{profile}{default}{password},
381 root 1.1 mapw => $mapsize,
382     maph => $mapsize,
383 root 1.11
384     map_widget => $MAPWIDGET,
385     logview => $LOGVIEW,
386     statusbox => $STATUSBOX,
387     map => $MAP,
388     mapmap => $MAPMAP,
389 root 1.60 query => \&server_query,
390 root 1.11
391     sound_play => sub {
392     my ($x, $y, $soundnum, $type) = @_;
393    
394     $SDL_MIXER
395     or return;
396    
397     my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
398     or return;
399    
400     $chunk->play;
401     },
402 root 1.1 };
403    
404     if ($CONN) {
405     CFClient::lowdelay fileno $CONN->{fh};
406    
407     status "login successful";
408     } else {
409     status "unable to connect";
410     stop_game();
411     }
412     }
413    
414     sub stop_game {
415 root 1.23 $LOGIN_BUTTON->set_text ("Login");
416 root 1.53 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER);
417 root 1.49 $SETUP_DIALOG->show;
418 elmex 1.85 $PL_WINDOW->hide;
419 root 1.86 $SPELL_PAGE->clear_spells;
420 root 1.23
421 root 1.1 return unless $CONN;
422    
423     status "connection closed";
424 root 1.23
425 root 1.60 destroy_query_dialog $CONN;
426 root 1.1 $CONN->destroy;
427     $CONN = 0; # false, does not autovivify
428 root 1.76
429     undef $MAP;
430 root 1.1 }
431    
432 root 1.49 sub graphics_setup {
433     my $vbox = new CFClient::UI::VBox;
434 root 1.30
435 root 1.1 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
436    
437     $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode");
438     $table->add (1, 0, my $hbox = new CFClient::UI::HBox);
439    
440 root 1.31 $hbox->add (my $mode_slider = new CFClient::UI::Slider force_w => $WIDTH * 0.1, expand => 1, range => [$CFG->{sdl_mode}, 0, $#SDL_MODES, 0, 1]);
441 root 1.1 $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999");
442    
443     $mode_slider->connect (changed => sub {
444     my ($self, $value) = @_;
445    
446     $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
447     $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]});
448     });
449     $mode_slider->emit (changed => $mode_slider->{range}[0]);
450    
451     my $row = 1;
452    
453     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fullscreen");
454 root 1.94 $table->add (1, $row++, $FULLSCREEN_ENABLE = new CFClient::UI::CheckBox
455 root 1.1 state => $CFG->{fullscreen},
456     tooltip => "Bring the client into fullscreen mode.",
457 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 }
458 root 1.1 );
459    
460     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
461     $table->add (1, $row++, new CFClient::UI::CheckBox
462     state => $CFG->{fast},
463     tooltip => "Lower the visual quality considerably to speed up rendering.",
464 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fast} = $value; 0 }
465 root 1.1 );
466    
467     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Map Scale");
468     $table->add (1, $row++, new CFClient::UI::Slider
469     range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
470     tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
471 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 }
472 root 1.1 );
473    
474     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War");
475     $table->add (1, $row++, new CFClient::UI::CheckBox
476     state => $CFG->{fow_enable},
477     tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.",
478 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 }
479 root 1.1 );
480    
481     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Intensity");
482     $table->add (1, $row++, new CFClient::UI::Slider
483     range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256],
484     tooltip => "<b>Fog of War Lightness.</b> The higher the intensity, the lighter the Fog-of-War color. Changes are instant.",
485 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_intensity} = $value; 0 }
486 root 1.1 );
487    
488     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Smooth");
489     $table->add (1, $row++, new CFClient::UI::CheckBox
490     state => $CFG->{fow_smooth},
491     tooltip => "Smooth the Fog-of-War a bit to make it more realistic. Changes are instant.",
492 root 1.18 on_changed => sub {
493 root 1.1 my ($self, $value) = @_;
494     $CFG->{fow_smooth} = $value;
495 root 1.15 status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFClient::OpenGL::GL_VERSION < 1.2;
496 root 1.74 0
497 root 1.1 }
498     );
499    
500     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
501     $table->add (1, $row++, new CFClient::UI::Slider
502     range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
503     tooltip => "The base font size used by most GUI elements that do not have their own setting.",
504 root 1.74 on_changed => sub { $CFG->{gui_fontsize} = $_[1]; 0 },
505 root 1.1 );
506    
507     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Message Fontsize");
508     $table->add (1, $row++, new CFClient::UI::Slider
509     range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1],
510     tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant.",
511 root 1.74 on_changed => sub { $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 },
512 root 1.1 );
513    
514     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
515     $table->add (1, $row++, new CFClient::UI::Slider
516     range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1],
517     tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.",
518 root 1.18 on_changed => sub {
519 root 1.1 $CFG->{gauge_fontsize} = $_[1];
520     &set_gauge_window_fontsize;
521 root 1.74 0
522 root 1.1 }
523     );
524    
525     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size");
526     $table->add (1, $row++, new CFClient::UI::Slider
527 root 1.18 range => [$CFG->{gauge_size}, 0.2, 0.8],
528     tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.",
529     on_changed => sub {
530 root 1.1 $CFG->{gauge_size} = $_[1];
531     $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
532 root 1.74 0
533 root 1.1 }
534     );
535    
536     $table->add (1, $row++, new CFClient::UI::Button
537     expand => 1, align => 0, text => "Apply",
538     tooltip => "Apply the video settings",
539 root 1.18 on_activate => sub {
540 root 1.1 video_shutdown ();
541     video_init ();
542 root 1.74 0
543 root 1.1 }
544     );
545    
546 root 1.49 $vbox
547     }
548    
549     sub audio_setup {
550     my $vbox = new CFClient::UI::VBox;
551    
552     $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
553    
554     my $row = 0;
555    
556 root 1.1 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Audio Enable");
557     $table->add (1, $row++, new CFClient::UI::CheckBox
558     state => $CFG->{audio_enable},
559     tooltip => "<b>Master Audio Enable.</b> If enabled, sound effects and music will be played. If disabled, no audio will be used and the soundcard will not be opened.",
560 root 1.74 on_changed => sub { $CFG->{audio_enable} = $_[1]; 0 }
561 root 1.1 );
562     # $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Effects Volume");
563 root 1.18 # $table->add (1, 8, new CFClient::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
564 root 1.1 # $CFG->{effects_volume} = $_[1];
565     # });
566     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Background Music");
567     $table->add (1, $row++, my $hbox = new CFClient::UI::HBox);
568     $hbox->add (new CFClient::UI::CheckBox
569     expand => 1, state => $CFG->{bgm_enable},
570     tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
571 root 1.74 on_changed => sub { $CFG->{bgm_enable} = $_[1]; 0 }
572 root 1.1 );
573     $hbox->add (new CFClient::UI::Slider
574     expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
575     tooltip => "The volume of the background music. Changes are instant.",
576 root 1.74 on_changed => sub { $CFG->{bgm_volume} = $_[1]; CFClient::MixMusic::volume $_[1] * 128; 0 }
577 root 1.1 );
578    
579     $table->add (1, $row++, new CFClient::UI::Button
580     expand => 1, align => 0, text => "Apply",
581     tooltip => "Apply the audio settings",
582 root 1.18 on_activate => sub {
583 root 1.1 audio_shutdown ();
584     audio_init ();
585 root 1.74 0
586 root 1.1 }
587     );
588    
589 root 1.49 $vbox
590 root 1.1 }
591    
592     sub set_gauge_window_fontsize {
593     for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
594     $_->set_fontsize ($::CFG->{gauge_fontsize});
595     }
596     }
597    
598     sub make_gauge_window {
599     my $gh = int $HEIGHT * $CFG->{gauge_size};
600    
601     my $win = new CFClient::UI::Frame (
602 root 1.30 force_x => 0,
603     force_y => "max",
604     force_w => $WIDTH,
605     force_h => $gh,
606 root 1.1 );
607    
608     $win->add (my $hbox = new CFClient::UI::HBox
609     children => [
610     (new CFClient::UI::HBox expand => 1),
611     (new CFClient::UI::VBox children => [
612     (new CFClient::UI::Empty expand => 1),
613 root 1.2 (new CFClient::UI::Frame bg => [0, 0, 0, 0.4], child => ($FLOORBOX = new CFClient::UI::Table)),
614 root 1.1 ]),
615     (my $vbox = new CFClient::UI::VBox),
616     ],
617     );
618    
619     $vbox->add (new CFClient::UI::HBox
620     expand => 1,
621     children => [
622     (new CFClient::UI::Empty expand => 1),
623     (my $hb = new CFClient::UI::HBox),
624     ],
625     );
626    
627     $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp',
628     tooltip => "<b>Health points</b>. Measures of how much damage you can take before dying. Hit points are determined from your level and are influenced by the value of your Con. Hp value may range between 1 to beyond 500 and higher values indicate a greater ability to withstand punishment.");
629     $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana',
630     tooltip => "<b>Spell points</b>. Measures of how much \"fuel\" you have for casting spells and incantations. Mana is calculated from your level and your Pow. Mana values can range between 1 to beyond 500 (glowing crystals can increase the current spell points beyond your normal maximum). Higher values indicate greater amounts of mana.");
631     $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace',
632     tooltip => "<b>Grace points</b> - how favored you are by your god. In game terms, how much divine magic you can cast. Your level, Wis and Pow effect what the value of grace is. Prayong on an altar of your god can increase this value beyond your normal maximum. Grace can take on large positive and negative values. Positive values indicate favor by the gods.");
633     $hb->add (my $fg = new CFClient::UI::Gauge type => 'food',
634     tooltip => "<b>Food</b>. Ranges between 0 (starving) and 999 (satiated). At a value of 0 the character begins to die. Some magic can speed up or slow down the character digestion. Healing wounds will speed up digestion too.");
635    
636     $vbox->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
637     tooltip => "<b>Experience points and overall level</b> - experience is increased as a reward for appropriate action (such as killing monsters) and may decrease as a result of a magical attack or dying. Level is directly derived from the experience value. As the level of the character increases, the character becomes able to succeed at more difficult tasks. A character's level starts at a value of 0 and may range up beyond 100.");
638     $vbox->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
639     tooltip => "<b>Ranged attack</b> - how you attack when you press shift-cursor (spell, skill, weapon etc.)");
640    
641     $GAUGES = {
642     exp => $exp, win => $win, range => $rng,
643     food => $fg, mana => $mg, hp => $hg, grace => $gg
644     };
645    
646     &set_gauge_window_fontsize;
647    
648     $win
649     }
650    
651 root 1.65 sub debug_setup {
652     my $table = new CFClient::UI::Table;
653    
654     $table->add (0, 0, new CFClient::UI::Label text => "Widget Borders");
655 root 1.74 $table->add (1, 0, new CFClient::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 1; 0 });
656 root 1.65 $table->add (0, 1, new CFClient::UI::Label text => "Tooltip Widget Info");
657 root 1.74 $table->add (1, 1, new CFClient::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 2; 0 });
658 root 1.65 $table->add (0, 2, new CFClient::UI::Label text => "Show FPS");
659 root 1.74 $table->add (1, 2, new CFClient::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 4; 0 });
660 root 1.65 $table->add (0, 3, new CFClient::UI::Label text => "Suppress Tooltips");
661 root 1.74 $table->add (1, 3, new CFClient::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 });
662 root 1.65
663     my @default_smooth = (0.05, 0.13, 0.05, 0.13, 0.30, 0.13, 0.05, 0.13, 0.05);
664    
665     for my $x (0..2) {
666     for my $y (0 .. 2) {
667     $table->add ($x + 3, $y,
668     new CFClient::UI::Entry
669     text => $default_smooth[$x * 3 + $y],
670     on_changed => sub { $MAP->{smooth_matrix}[$x * 3 + $y] = $_[1] if $MAP; 0 },
671     );
672     }
673     }
674    
675    
676     $table
677     }
678 elmex 1.24
679 root 1.60 sub stats_window {
680 root 1.98 my $r = new CFClient::UI::ScrolledWindow (
681     expand => 1,
682     scroll_y => 1
683     );
684 elmex 1.97 $r->add (my $vb = new CFClient::UI::VBox);
685 root 1.1
686     $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1,
687     can_hover => 1, can_events => 1,
688     tooltip => "Your name and title. You can change your title by using the <b>title</b> command, if supported by the server.");
689     $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1,
690     can_hover => 1, can_events => 1,
691     tooltip => "The map you are currently on (if supported by the server).");
692    
693 elmex 1.5 $vb->add (my $hb0 = new CFClient::UI::HBox);
694     $hb0->add ($STATWIDS->{weight} = new CFClient::UI::Label valign => 0, align => -1, text => "Weight:", expand => 1,
695     can_hover => 1, can_events => 1,
696 root 1.15 tooltip => "The weight of the player including all inventory items.");
697 elmex 1.5 $hb0->add ($STATWIDS->{m_weight} = new CFClient::UI::Label valign => 0, align => -1, text => "Max weight:", expand => 1,
698     can_hover => 1, can_events => 1,
699 root 1.15 tooltip => "The weight limit: you cannot carry more than this.");
700 elmex 1.5
701 root 1.1 $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
702     $hb->add (my $tbl = new CFClient::UI::Table expand => 1);
703    
704     my $color2 = [1, 1, 0];
705    
706     for (
707 root 1.98 [0, 0, st_str => "Str", 30],
708     [0, 1, st_dex => "Dex", 30],
709     [0, 2, st_con => "Con", 30],
710     [0, 3, st_int => "Int", 30],
711     [0, 4, st_wis => "Wis", 30],
712     [0, 5, st_pow => "Pow", 30],
713     [0, 6, st_cha => "Cha", 30],
714    
715     [2, 0, st_wc => "Wc", -120],
716     [2, 1, st_ac => "Ac", -120],
717     [2, 2, st_dam => "Dam", 120],
718     [2, 3, st_arm => "Arm", 120],
719     [2, 4, st_spd => "Spd", 10.54],
720     [2, 5, st_wspd => "WSp", 10.54],
721 root 1.1 ) {
722 root 1.98 my ($col, $row, $id, $label, $template) = @$_;
723 root 1.1
724     $tbl->add ($col , $row, $STATWIDS->{$id} = new CFClient::UI::Label
725 root 1.98 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0,
726     align => +1, template => $template, tooltip => $CFClient::STAT_TOOLTIP{$label});
727 root 1.1 $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFClient::UI::Label
728 root 1.98 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0,
729     align => -1, text => $label, tooltip => $CFClient::STAT_TOOLTIP{$label});
730 root 1.1 }
731    
732 elmex 1.92 $vb->add (my $tbl2 = new CFClient::UI::Table expand => 1);
733 root 1.1
734     my $row = 0;
735     my $col = 0;
736    
737     my %resist_names = (
738 elmex 1.92 slow => ["Slow",
739     "<b>Slow</b> (slows you down when you are hit by the spell. Monsters will have an opportunity to come near you faster and hit you more often.)"],
740     holyw => ["Holy Word",
741     "<b>Holy Word</b> (resistance you against getting the fear when someone whose god doesn't like you spells the holy word on you.)"],
742     conf => ["Confusion",
743     "<b>Confusion</b> (If you are hit by confusion you will move into random directions, and likely into monsters.)"],
744     fire => ["Fire",
745     "<b>Fire</b> (just your resistance to fire spells like burning hands, dragonbreath, meteor swarm fire, ...)"],
746     depl => ["Depletion",
747     "<b>Depletion</b> (some monsters and other effects can cause stats depletion)"],
748     magic => ["Magic",
749     "<b>Magic</b> (resistance to magic spells like magic missile or similar)"],
750     drain => ["Draining",
751     "<b>Draining</b> (some monsters (e.g. vampires) and other effects can steal experience)"],
752     acid => ["Acid",
753     "<b>Acid</b> (resistance to acid, acid hurts pretty much and also corrodes your weapons)"],
754     pois => ["Poison",
755     "<b>Poison</b> (resistance to getting poisoned)"],
756     para => ["Paralysation",
757     "<b>Paralysation</b> (this resistance affects the chance you get paralysed)"],
758     deat => ["Death",
759     "<b>Death</b> (resistance against death spells)"],
760     phys => ["Physical",
761     "<b>Physical</b> (this is the resistance against physical attacks, like when a monster hit you in melee combat. The value displayed here is also displayed in the 'Arm' field on the left.)"],
762     blind => ["Blind",
763     "<b>Blind</b> (blind resistance affects the chance of a successful blinding attack)"],
764     fear => ["Fear",
765     "<b>Fear</b> (this attack will drive you away from monsters who cast this and hit you successfully, being resistant to this helps a lot when fighting those monsters)"],
766     tund => ["Turn undead",
767     "<b>Turn undead</b> (affects your resistancy to various forms of 'turn undead' spells. Only relevant when you are, in fact, undead..."],
768     elec => ["Electricity",
769     "<b>Electricity</b> (resistance against electricity, spells like large lightning, small lightning, ...)"],
770     cold => ["Cold",
771     "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)"],
772     ghit => ["Ghost hit",
773     "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)"],
774 root 1.1 );
775     for (qw/slow holyw conf fire depl magic
776     drain acid pois para deat phys
777     blind fear tund elec cold ghit/)
778     {
779     $tbl2->add ($col, $row,
780     $STATWIDS->{"res_$_"} =
781     new CFClient::UI::Label
782     font => $FONT_FIXED,
783     template => "-100%",
784     align => +1,
785     valign => 0,
786     can_events => 1,
787     can_hover => 1,
788 elmex 1.92 tooltip => $resist_names{$_}->[1],
789 root 1.1 );
790     $tbl2->add ($col + 1, $row, new CFClient::UI::Image
791     font => $FONT_FIXED,
792     can_hover => 1,
793     can_events => 1,
794 root 1.78 path => "ui/resist/resist_$_.png",
795 elmex 1.92 tooltip => $resist_names{$_}->[1],
796     );
797     $tbl2->add ($col + 2, $row, new CFClient::UI::Label
798     text => $resist_names{$_}->[0],
799     font => $FONT_FIXED,
800     can_hover => 1,
801     can_events => 1,
802     tooltip => $resist_names{$_}->[1],
803 root 1.1 );
804    
805     $row++;
806     if ($row % 6 == 0) {
807 elmex 1.92 $col += 3;
808 root 1.1 $row = 0;
809     }
810     }
811    
812 root 1.95 #update_stats_window ({});
813 root 1.1
814 elmex 1.97 $r
815 root 1.1 }
816    
817 elmex 1.92 sub skill_window {
818 elmex 1.97 my $sw = new CFClient::UI::ScrolledWindow (expand => 1);
819     $sw->add ($STATWIDS->{skill_tbl} = new CFClient::UI::Table expand => 1, col_expand => [0, 0, 1, 0, 0, 1]);
820     $sw
821 elmex 1.92 }
822    
823 root 1.48 sub formsep($) {
824     scalar reverse join ",", unpack "(A3)*", reverse $_[0] * 1
825 root 1.1 }
826    
827     my $METASERVER_ATIME;
828    
829     sub update_metaserver {
830 elmex 1.81 my ($metaserver_dialog) = @_;
831    
832     $METASERVER = $metaserver_dialog
833     if defined $metaserver_dialog;
834    
835 root 1.1 return if $METASERVER_ATIME > time;
836     $METASERVER_ATIME = time + 60;
837    
838     my $table = $METASERVER->{table};
839     $table->clear;
840     $table->add (0, 0, my $label = new CFClient::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
841    
842     my $buf;
843    
844     my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0;
845    
846     unless ($fh) {
847     $label->set_text ("unable to contact metaserver: $!");
848     return;
849     }
850    
851     Event->io (fd => $fh, poll => 'r', cb => sub {
852     my $res = sysread $fh, $buf, 8192, length $buf;
853    
854     if (!defined $res) {
855     $_[0]->w->cancel;
856     $label->set_text ("error while retrieving server list: $!");
857     } elsif ($res == 0) {
858     $_[0]->w->cancel;
859     status "server list retrieved";
860    
861     utf8::decode $buf if utf8::valid $buf;
862    
863     $table->clear;
864    
865 root 1.62 my @tip = (
866     "The current number of users logged in on the server.",
867     "The hostname of the server.",
868     "The time this server has been running without being restarted.",
869     "The server software version - a '+' indicates a Crossfire+ server.",
870     "Short information about this server provided by its admins.",
871     );
872     my @col = qw(#Users Host Uptime Version Description);
873     $table->add ($_, 0, new CFClient::UI::Label
874     can_hover => 1, can_events => 1,
875     align => 0, fg => [1, 1, 0],
876     text => $col[$_], tooltip => $tip[$_])
877     for 0 .. $#col;
878 root 1.1
879     my @align = qw(1 0 1 1 -1);
880    
881     my $y = 0;
882     for my $m (sort { $b->[3] <=> $a->[3] } map [split /\|/], split /\015?\012/, $buf) {
883     my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) = @$m;
884    
885     for ($desc) {
886     s/<br>/\n/gi;
887     s/<li>/\n· /gi;
888     s/<.*?>//sgi;
889     s/&/&amp;/g;
890     s/</&lt;/g;
891     s/>/&gt;/g;
892     }
893    
894     $uptime = sprintf "%dd %02d:%02d:%02d",
895     (int $m->[8] / 86400),
896     (int $m->[8] / 3600) % 24,
897     (int $m->[8] / 60) % 60,
898     $m->[8] % 60;
899    
900     $m = [$users, $host, $uptime, $version, $desc];
901    
902     $y++;
903    
904 root 1.62 $table->add (scalar @$m, $y, new CFClient::UI::VBox children => [
905     (new CFClient::UI::Button
906     text => "Use",
907     tooltip => "Put this server into the <b>Host:Port</b> field",
908     on_activate => sub {
909 root 1.75 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host);
910 root 1.62 $METASERVER->hide;
911 root 1.74 0
912 root 1.62 },
913     ),
914 root 1.1 (new CFClient::UI::Empty expand => 1),
915     ]);
916    
917 root 1.62 $table->add ($_, $y, new CFClient::UI::Label
918     ellipsise => 0,
919     align => $align[$_],
920     text => $m->[$_],
921     tooltip => $tip[$_],
922     can_hover => 1,
923     can_events => 1,
924     fontsize => 0.8)
925 root 1.1 for 0 .. $#$m;
926     }
927     }
928     });
929     }
930    
931 root 1.40 sub metaserver_dialog {
932 elmex 1.81 my $vbox = new CFClient::UI::VBox;
933     my $table = new CFClient::UI::Table;
934     $vbox->add (new CFClient::UI::ScrolledWindow expand => 1, child => $table);
935    
936 root 1.40 my $dialog = new CFClient::UI::FancyFrame
937 root 1.62 title => "Server List",
938     name => 'metaserver_dialog',
939     x => 'center',
940     y => 'center',
941     z => 3,
942     force_h => $::HEIGHT * 0.4,
943 elmex 1.81 child => $vbox,
944 root 1.80 has_close_button => 1,
945 elmex 1.81 table => $table,
946 root 1.40 on_visibility_change => sub {
947 elmex 1.81 update_metaserver ($_[0]) if $_[1];
948 root 1.74 0
949 root 1.40 },
950     ;
951    
952     $dialog
953     }
954    
955 root 1.1 sub server_setup {
956 root 1.49 my $vbox = new CFClient::UI::VBox;
957 elmex 1.19
958 root 1.1 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
959     $table->add (0, 2, new CFClient::UI::Label valign => 0, align => 1, text => "Host:Port");
960    
961     {
962     $table->add (1, 2, my $vbox = new CFClient::UI::VBox);
963    
964     $vbox->add (
965 root 1.40 $HOST_ENTRY = new CFClient::UI::Entry
966 root 1.1 expand => 1,
967 root 1.75 text => $CFG->{profile}{default}{host},
968 root 1.1 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
969 root 1.18 on_changed => sub {
970 root 1.1 my ($self, $value) = @_;
971 root 1.75 $CFG->{profile}{default}{host} = $value;
972 root 1.74 0
973 root 1.1 }
974     );
975    
976 root 1.40 $vbox->add (new CFClient::UI::Button
977     expand => 1,
978     text => "Server List",
979     other => $METASERVER,
980 root 1.1 tooltip => "Show a list of available crossfire servers",
981 root 1.74 on_activate => sub { $METASERVER->toggle_visibility; 0 },
982     on_visibility_change => sub { $METASERVER->hide unless $_[1]; 0 },
983 root 1.1 );
984     }
985    
986     $table->add (0, 4, new CFClient::UI::Label valign => 0, align => 1, text => "Username");
987     $table->add (1, 4, new CFClient::UI::Entry
988 root 1.75 text => $CFG->{profile}{default}{user},
989 root 1.1 tooltip => "The name of your character on the server",
990 root 1.75 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value }
991 root 1.1 );
992    
993     $table->add (0, 5, new CFClient::UI::Label valign => 0, align => 1, text => "Password");
994     $table->add (1, 5, new CFClient::UI::Entry
995 root 1.75 text => $CFG->{profile}{default}{password},
996 root 1.1 hidden => 1,
997     tooltip => "The password for your character",
998 root 1.75 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value }
999 root 1.1 );
1000    
1001     $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size");
1002     $table->add (1, 7, new CFClient::UI::Slider
1003 root 1.30 force_w => 100,
1004 root 1.1 range => [$CFG->{mapsize}, 10, 100, 0, 1],
1005     tooltip => "This is the size of the portion of the map update the server sends you. "
1006     . "If you set this to a high value you will be able to see further, "
1007     . "but you also increase bandwidth requirements and latency. "
1008     . "This option is only used once at log-in.",
1009 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 0 },
1010 root 1.1 );
1011    
1012     $table->add (0, 8, new CFClient::UI::Label valign => 0, align => 1, text => "Face Prefetch");
1013     $table->add (1, 8, new CFClient::UI::CheckBox
1014     state => $CFG->{face_prefetch},
1015     tooltip => "<b>Background Image Prefetch</b>\n\n"
1016     . "If enabled, the client automatically pre-fetches images from the server. "
1017     . "This might increase or create lag, but increases the chances "
1018     . "of faces being ready for display when you encounter them. "
1019     . "It also uses up server bandwidth on every connect, "
1020     . "so only set it if you really need to prefetch images. "
1021     . "This option can be set and unset any time.",
1022 root 1.74 on_changed => sub { $CFG->{face_prefetch} = $_[1]; 0 },
1023 root 1.1 );
1024    
1025     $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Output-Count");
1026     $table->add (1, 9, new CFClient::UI::Entry
1027     text => $CFG->{output_count},
1028     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1029 root 1.74 on_changed => sub { $CFG->{output_count} = $_[1]; 0 },
1030 root 1.1 );
1031    
1032     $table->add (0, 10, new CFClient::UI::Label valign => 0, align => 1, text => "Output-Sync");
1033     $table->add (1, 10, new CFClient::UI::Entry
1034     text => $CFG->{output_sync},
1035     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1036 root 1.74 on_changed => sub { $CFG->{output_sync} = $_[1]; 0 },
1037 root 1.1 );
1038    
1039     $table->add (1, 11, $LOGIN_BUTTON = new CFClient::UI::Button
1040     expand => 1,
1041     align => 0,
1042     text => "Login",
1043 root 1.18 on_activate => sub {
1044 root 1.1 $CONN ? stop_game
1045     : start_game;
1046 root 1.74 0
1047 root 1.1 },
1048     );
1049    
1050 root 1.49 $table->add (0, 12, new CFClient::UI::Label valign => 0, align => 1, text => "Chat Command");
1051     $table->add (1, 12, my $saycmd = new CFClient::UI::Entry
1052     text => $CFG->{say_command},
1053     tooltip => "This is the command that will be used if you write a line in the message window entry or press <b>\"</b> in the map window. "
1054     . "Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
1055     . "But you could also set it to <b>tell <i>playername</i></b> to only chat with that user.",
1056     on_changed => sub {
1057     my ($self, $value) = @_;
1058     $CFG->{say_command} = $value;
1059 root 1.74 0
1060 root 1.49 }
1061     );
1062    
1063 root 1.67 $vbox->add (new CFClient::UI::Label
1064     text => "Server Info",
1065     fontsize => 1.2,
1066     padding_y => 8,
1067     fg => [1, 1, 0, 1],
1068     );
1069    
1070     $vbox->add ($SERVER_INFO = new CFClient::UI::Label ellipsise => 0);
1071    
1072 root 1.49 $vbox
1073 root 1.1 }
1074    
1075     sub message_window {
1076     my $window = new CFClient::UI::FancyFrame
1077 elmex 1.16 name => "message_window",
1078 root 1.1 title => "Messages",
1079     border_bg => [1, 1, 1, 1],
1080 root 1.30 x => "max",
1081     y => 0,
1082 root 1.60 force_w => $::WIDTH * 0.4,
1083     force_h => $::HEIGHT * 0.5,
1084 root 1.74 child => (my $vbox = new CFClient::UI::VBox),
1085     has_close_button => 1;
1086 root 1.1
1087     $vbox->add ($LOGVIEW);
1088    
1089     $vbox->add (my $input = new CFClient::UI::Entry
1090     tooltip => "<b>Chat Box</b>. If you enter a text and press return/enter here, the current <i>communication command</i> "
1091     . "from the client setup will be prepended (e.g. <b>shout</b>, <b>chat</b>...). "
1092     . "If you prepend a slash (/), you will submit a command instead (similar to IRC). "
1093     . "A better way to submit commands (and the occasional chat command) is often the map command completer.",
1094 root 1.18 on_focus_in => sub {
1095 root 1.1 my ($input, $prev_focus) = @_;
1096    
1097     delete $input->{refocus_map};
1098    
1099     if ($prev_focus == $MAPWIDGET && $input->{auto_activated}) {
1100     $input->{refocus_map} = 1;
1101     }
1102     delete $input->{auto_activated};
1103 root 1.74
1104     0
1105 root 1.1 },
1106 root 1.18 on_activate => sub {
1107 root 1.1 my ($input, $text) = @_;
1108     $input->set_text ('');
1109    
1110 elmex 1.46 if ($text =~ /^\/(.*)/) {
1111 root 1.1 $::CONN->user_send ($1);
1112     } else {
1113     my $say_cmd = $::CFG->{say_command} || 'say';
1114     $::CONN->user_send ("$say_cmd $text");
1115     }
1116     if ($input->{refocus_map}) {
1117     delete $input->{refocus_map};
1118     $MAPWIDGET->focus_in
1119     }
1120 root 1.74
1121     0
1122 root 1.1 },
1123 root 1.18 on_escape => sub {
1124 root 1.74 $MAPWIDGET->grab_focus;
1125    
1126     0
1127 root 1.1 },
1128     );
1129    
1130     $CONSOLE = {
1131     window => $window,
1132 root 1.30 input => $input,
1133 root 1.1 };
1134    
1135     $window
1136     }
1137    
1138     sub open_quit_dialog {
1139     unless ($QUIT_DIALOG) {
1140 root 1.30 $QUIT_DIALOG = new CFClient::UI::FancyFrame
1141     x => "center",
1142     y => "center",
1143 root 1.55 z => 50,
1144 root 1.30 title => "Really Quit?",
1145 elmex 1.101 on_key_down => sub {
1146     my ($dialog, $ev) = @_;
1147     $ev->{sym} == 27 and $dialog->hide;
1148     }
1149 root 1.30 ;
1150 root 1.1
1151     $QUIT_DIALOG->add (my $vb = new CFClient::UI::VBox expand => 1);
1152    
1153     $vb->add (new CFClient::UI::Label
1154     text => "You should find a savebed and apply it first!",
1155     max_w => $WIDTH * 0.25,
1156     ellipsize => 0,
1157     );
1158     $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
1159     $hb->add (new CFClient::UI::Button
1160     text => "Ok",
1161     expand => 1,
1162 root 1.74 on_activate => sub { $QUIT_DIALOG->hide; 0 },
1163 root 1.1 );
1164     $hb->add (new CFClient::UI::Button
1165     text => "Quit anyway",
1166     expand => 1,
1167 root 1.18 on_activate => sub { exit },
1168 root 1.1 );
1169 root 1.21 }
1170 root 1.1
1171 root 1.21 $QUIT_DIALOG->show;
1172 elmex 1.101 $QUIT_DIALOG->grab_focus;
1173 root 1.1 }
1174    
1175 root 1.49 sub autopickup_setup {
1176 root 1.51 my $table = new CFClient::UI::Table;
1177 elmex 1.44
1178 elmex 1.43 for (
1179 root 1.51 ["General", 0, 0,
1180 root 1.86 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE],
1181 root 1.58 ["Inhibit autopickup" => PICKUP_INHIBIT],
1182     ["Stop before pickup" => PICKUP_STOP],
1183     ["Debug autopickup" => PICKUP_DEBUG],
1184 root 1.51 ],
1185     ["Weapons", 0, 6,
1186 root 1.58 ["All weapons" => PICKUP_ALLWEAPON],
1187     ["Missile weapons" => PICKUP_MISSILEWEAPON],
1188     ["Bows" => PICKUP_BOW],
1189     ["Arrows" => PICKUP_ARROW],
1190 root 1.51 ],
1191     ["Armour", 0, 12,
1192 root 1.58 ["Helmets" => PICKUP_HELMET],
1193     ["Shields" => PICKUP_SHIELD],
1194     ["Body Armour" => PICKUP_ARMOUR],
1195     ["Boots" => PICKUP_BOOTS],
1196     ["Gloves" => PICKUP_GLOVES],
1197     ["Cloaks" => PICKUP_CLOAK],
1198 root 1.51 ],
1199    
1200     ["Readables", 2, 2,
1201 root 1.58 ["Spellbooks" => PICKUP_SPELLBOOK],
1202     ["Skillscrolls" => PICKUP_SKILLSCROLL],
1203     ["Normal Books/Scrolls" => PICKUP_READABLES],
1204 root 1.51 ],
1205     ["Misc", 2, 7,
1206 root 1.58 ["Food" => PICKUP_FOOD],
1207     ["Drinks" => PICKUP_DRINK],
1208     ["Valuables (Money, Gems)" => PICKUP_VALUABLES],
1209     ["Keys" => PICKUP_KEY],
1210     ["Magical Items" => PICKUP_MAGICAL],
1211     ["Potions" => PICKUP_POTION],
1212     ["Magic Devices" => PICKUP_MAGIC_DEVICE],
1213     ["Ignore cursed" => PICKUP_NOT_CURSED],
1214     ["Jewelery" => PICKUP_JEWELS],
1215 root 1.51 ],
1216 elmex 1.66 ["Weight/Value ratio", 2, 17]
1217 elmex 1.43 )
1218     {
1219 root 1.51 my ($title, $x, $y, @bits) = @$_;
1220     $table->add ($x, $y, new CFClient::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
1221    
1222     for (@bits) {
1223     ++$y;
1224    
1225 elmex 1.43 my $mask = $_->[1];
1226 root 1.51 $table->add ($x , $y, new CFClient::UI::Label text => $_->[0], align => 1, expand => 1);
1227 root 1.86 $table->add ($x+1, $y, my $checkbox = new CFClient::UI::CheckBox
1228 elmex 1.83 state => $::CFG->{pickup} & $mask,
1229 elmex 1.43 on_changed => sub {
1230     my ($box, $value) = @_;
1231 root 1.63
1232 elmex 1.43 if ($value) {
1233 elmex 1.45 $::CFG->{pickup} |= $mask;
1234 elmex 1.43 } else {
1235 root 1.63 $::CFG->{pickup} &= ~$mask;
1236 elmex 1.43 }
1237 root 1.63
1238     $::CONN->send_command ("pickup $::CFG->{pickup}")
1239 elmex 1.45 if defined $::CONN;
1240 root 1.74
1241     0
1242 elmex 1.43 });
1243 root 1.86
1244     ${$_->[2]} = $checkbox if $_->[2];
1245 elmex 1.43 }
1246     }
1247    
1248 elmex 1.66 $table->add (2, 18, new CFClient::UI::ValSlider
1249 elmex 1.83 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1],
1250     template => ">= 99",
1251 elmex 1.66 to_value => sub { ">= " . 5 * $_[0] },
1252     on_changed => sub {
1253     my ($slider, $value) = @_;
1254    
1255 elmex 1.83 $::CFG->{pickup} &= ~0xF;
1256 elmex 1.66 $::CFG->{pickup} |= int $value
1257     if $value;
1258     1;
1259     });
1260 elmex 1.83
1261 elmex 1.66 $table->add (3, 18, new CFClient::UI::Button
1262     text => "set",
1263     on_activate => sub {
1264     $::CONN->send_command ("pickup $::CFG->{pickup}")
1265     if defined $::CONN;
1266 root 1.74 0
1267 elmex 1.66 });
1268    
1269 root 1.51 $table
1270 elmex 1.43 }
1271    
1272 root 1.102 my %SORT_ORDER = (
1273     type => undef,
1274     mtime => sub { sort {
1275     ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1276     or $b->{mtime} <=> $a->{mtime}
1277     or $a->{type} <=> $b->{type}
1278     } @_ },
1279     weight => sub { sort {
1280     $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1281     or $a->{type} <=> $b->{type}
1282     } @_ },
1283     );
1284    
1285 elmex 1.85 sub inventory_widget {
1286     my $hb = new CFClient::UI::HBox homogeneous => 1;
1287 root 1.1
1288 root 1.21 $hb->add (my $vb1 = new CFClient::UI::VBox);
1289     $vb1->add (new CFClient::UI::Label align => 0, text => "Player");
1290 root 1.99
1291     $vb1->add (my $hb1 = new CFClient::UI::HBox);
1292    
1293     use sort 'stable';
1294    
1295 root 1.102 $hb1->add (new CFClient::UI::Selector
1296     value => $::CFG->{inv_sort},
1297 root 1.99 options => [
1298 root 1.102 [type => "Type/Name"],
1299     [mtime => "Recent/Normal/Locked"],
1300     [weight => "Weight/Type"],
1301 root 1.99 ],
1302     on_changed => sub {
1303 root 1.102 $::CFG->{inv_sort} = $_[1];
1304     $INV->set_sort_order ($SORT_ORDER{$_[1]});
1305 root 1.99 },
1306     );
1307     $hb1->add (new CFClient::UI::Label text => "Weight: ", align => 1, expand => 1);
1308     #TODO# update to weigh/maxweight
1309     $hb1->add ($STATWIDS->{i_weight} = new CFClient::UI::Label align => -1);
1310    
1311 elmex 1.97 $vb1->add (my $sw1 = new CFClient::UI::ScrolledWindow expand => 1, scroll_y => 1);
1312     $sw1->add ($INV = new CFClient::UI::Inventory);
1313 root 1.1
1314 root 1.21 $hb->add (my $vb2 = new CFClient::UI::VBox);
1315 elmex 1.17
1316 elmex 1.27 $vb2->add ($INV_RIGHT_HB = new CFClient::UI::HBox);
1317 elmex 1.14
1318 elmex 1.97 $vb2->add (my $sw2 = new CFClient::UI::ScrolledWindow expand => 1, scroll_y => 1);
1319     $sw2->add ($INVR = new CFClient::UI::Inventory);
1320 root 1.1
1321 elmex 1.27 # XXX: Call after $INVR = ... because set_opencont sets the items
1322     CFClient::Protocol::set_opencont ($::CONN, 0, "Floor");
1323    
1324 elmex 1.85 $hb
1325 root 1.1 }
1326    
1327 root 1.86 sub toggle_player_page {
1328     my ($widget) = @_;
1329    
1330     if ($PL_WINDOW->{visible} && $PL_NOTEBOOK->get_current_page == $widget) {
1331     $PL_WINDOW->hide;
1332     } else {
1333     $PL_NOTEBOOK->set_current_page ($widget);
1334     $PL_WINDOW->show;
1335     }
1336     }
1337    
1338 elmex 1.85 sub player_window {
1339     my $plwin = $PL_WINDOW = new CFClient::UI::FancyFrame
1340     x => "center",
1341     y => "center",
1342     force_w => $WIDTH * 9/10,
1343     force_h => $HEIGHT * 9/10,
1344     title => "Player",
1345 elmex 1.90 name => "playerbook",
1346 elmex 1.85 has_close_button => 1
1347     ;
1348    
1349     my $ntb =
1350     $PL_NOTEBOOK =
1351 elmex 1.97 new CFClient::UI::Notebook expand => 1, debug => 1;
1352 root 1.86
1353 elmex 1.85 $ntb->add (
1354 root 1.95 "Statistics (F2)" => $STATS_PAGE = stats_window,
1355 elmex 1.92 "Shows statistics, where all your Stats and Resistances are shown."
1356     );
1357     $ntb->add (
1358 root 1.95 "Skills (F3)" => $SKILL_PAGE = skill_window,
1359 elmex 1.92 "Shows all your Skills."
1360 elmex 1.85 );
1361 elmex 1.97
1362     my $spellsw = new CFClient::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1363     $spellsw->add ($SPELL_PAGE = new CFClient::UI::SpellList);
1364 elmex 1.85 $ntb->add (
1365 elmex 1.97 "Spellbook (F4)" => $spellsw,
1366 root 1.86 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1367 elmex 1.85 );
1368     $ntb->add (
1369 root 1.95 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1370 root 1.86 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1371     . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1372 elmex 1.85 );
1373    
1374 root 1.88 $ntb->set_current_page ($INVENTORY_PAGE);
1375 root 1.86
1376 elmex 1.85 $plwin->add ($ntb);
1377     $plwin
1378 elmex 1.38 }
1379    
1380 elmex 1.77 sub update_bindings {
1381     $BIND_UPD_CB->() if $BIND_UPD_CB;
1382     }
1383    
1384 root 1.49 sub keyboard_setup {
1385 elmex 1.24 my $binding_list = new CFClient::UI::VBox;
1386    
1387 elmex 1.34 my $refresh;
1388 elmex 1.77 $refresh = $BIND_UPD_CB = sub {
1389 elmex 1.24 $binding_list->clear ();
1390    
1391 root 1.75 for my $mod (keys %{$::CFG->{profile}{default}{bindings}}) {
1392     for my $sym (keys %{$::CFG->{profile}{default}{bindings}{$mod}}) {
1393     my $cmds = $::CFG->{profile}{default}{bindings}{$mod}{$sym};
1394 elmex 1.24 next unless ref $cmds eq 'ARRAY' and @$cmds > 0;
1395    
1396     my $lbl = join "; ", @$cmds;
1397 root 1.84 my $nam = CFClient::BindingEditor::keycombo_to_name ($mod, $sym);
1398 elmex 1.24 $binding_list->add (my $hb = new CFClient::UI::HBox);
1399     $hb->add (new CFClient::UI::Button
1400 elmex 1.25 text => "delete",
1401 elmex 1.34 tooltip => "Deletes the binding",
1402 elmex 1.24 on_activate => sub {
1403     $binding_list->remove ($hb);
1404 root 1.75 delete $::CFG->{profile}{default}{bindings}{$mod}{$sym};
1405 root 1.74 0
1406 elmex 1.24 });
1407 elmex 1.34
1408     $hb->add (new CFClient::UI::Button
1409     text => "edit",
1410     tooltip => "Edits the binding",
1411     on_activate => sub {
1412     $::BIND_EDITOR->set_binding (
1413 root 1.75 $mod, $sym, $::CFG->{profile}{default}{bindings}{$mod}{$sym},
1414 elmex 1.34 sub {
1415     my ($nmod, $nsym, $ncmds) = @_;
1416 elmex 1.77 $::BIND_EDITOR->cfg_unbind ($mod, $sym);
1417     $::BIND_EDITOR->cfg_bind ($nmod, $nsym, $ncmds);
1418 elmex 1.34 $refresh->();
1419 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1420     $SETUP_DIALOG->show;
1421 elmex 1.34 },
1422     sub {
1423 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1424     $SETUP_DIALOG->show;
1425 elmex 1.34 });
1426     $::BIND_EDITOR->show;
1427 root 1.49 $SETUP_DIALOG->hide;
1428 root 1.74 0
1429 elmex 1.34 });
1430    
1431     $hb->add (new CFClient::UI::Label text => "(Key: $nam)");
1432 elmex 1.24 $hb->add (new CFClient::UI::Label text => $lbl, expand => 1);
1433     }
1434     }
1435     };
1436    
1437 root 1.49 my $vb = new CFClient::UI::VBox;
1438 elmex 1.71 $vb->add (my $hb = new CFClient::UI::HBox);
1439     $hb->add (new CFClient::UI::Label text => "only shift-up stops fire");
1440     $hb->add (new CFClient::UI::CheckBox
1441     expand => 1,
1442     state => $CFG->{shift_fire_stop},
1443     tooltip => "If this checkbox is enabled you will stop fire only if you stop pressing shift",
1444     on_changed => sub {
1445     my ($cbox, $value) = @_;
1446     $CFG->{shift_fire_stop} = $value;
1447 root 1.74 0
1448 elmex 1.71 });
1449    
1450 elmex 1.35 $vb->add ($binding_list);
1451     $vb->add (my $hb = new CFClient::UI::HBox);
1452 root 1.49
1453 elmex 1.35 $hb->add (new CFClient::UI::Button
1454 elmex 1.34 text => "record new",
1455 elmex 1.35 expand => 1,
1456 elmex 1.34 tooltip => "This button opens the binding editor with an empty binding.",
1457     on_activate => sub {
1458     $::BIND_EDITOR->set_binding (undef, undef, [],
1459     sub {
1460     my ($mod, $sym, $cmds) = @_;
1461 elmex 1.77 $::BIND_EDITOR->cfg_bind ($mod, $sym, $cmds);
1462 elmex 1.34 $refresh->();
1463 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1464     $SETUP_DIALOG->show;
1465 elmex 1.34 },
1466     sub {
1467 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1468     $SETUP_DIALOG->show;
1469 root 1.53 },
1470     );
1471 root 1.49 $SETUP_DIALOG->hide;
1472 elmex 1.34 $::BIND_EDITOR->show;
1473 root 1.74 0
1474 elmex 1.34 },
1475     );
1476 root 1.49
1477 elmex 1.35 $hb->add (new CFClient::UI::Button
1478     text => "close",
1479     tooltip => "Closes the binding window",
1480     expand => 1,
1481     on_activate => sub {
1482 root 1.49 $SETUP_DIALOG->hide;
1483 root 1.74 0
1484 elmex 1.35 }
1485     );
1486    
1487 elmex 1.24 $refresh->();
1488 root 1.49
1489     $vb
1490 elmex 1.24 }
1491    
1492 root 1.64 sub help_window {
1493 root 1.1 my $win = new CFClient::UI::FancyFrame
1494 root 1.41 x => 'center',
1495     y => 'center',
1496 root 1.55 z => 2,
1497 root 1.41 name => 'doc_browser',
1498     force_w => int $WIDTH * 7/8,
1499     force_h => int $HEIGHT * 7/8,
1500 root 1.87 title => "Help Browser",
1501     has_close_button => 1;
1502 root 1.1
1503     $win->add (my $vbox = new CFClient::UI::VBox);
1504    
1505     $vbox->add (my $buttons = new CFClient::UI::HBox);
1506 root 1.64 $vbox->add (my $viewer = new CFClient::UI::TextScroller
1507     expand => 1, fontsize => 0.8, padding_x => 4);
1508 root 1.1
1509 root 1.64 $buttons->add (new CFClient::UI::Label text => "Choose a document to display: ");
1510 root 1.102 $buttons->add (my $combo = new CFClient::UI::Selector
1511 root 1.64 value => undef,
1512     options => [
1513     [intro => "Introduction"],
1514 root 1.78 [manual => "Main Manual"],
1515     [skill_help => "Skill Reference"],
1516     [command_help => "Command Reference"],
1517 root 1.64 [dmcommand_help => "DM Commands"],
1518     [COPYING => "License Terms"],
1519     ],
1520     on_changed => sub {
1521     my ($self, $pod) = @_;
1522 root 1.1
1523 root 1.64 $viewer->clear;
1524 root 1.103 $viewer->add_paragraph (@{ CFClient::Pod::pod_paragraphs $pod });
1525 root 1.64 $viewer->set_offset (0);
1526 root 1.78
1527 root 1.74 0
1528 root 1.64 },
1529     on_visibility_change => sub {
1530     my ($self, $visible) = @_;
1531     return unless $visible;
1532     return if $self->{value};
1533     $self->set_value ("intro");
1534 root 1.74 0
1535 root 1.64 },
1536     );
1537 root 1.1
1538     $win
1539     }
1540    
1541     sub sdl_init {
1542     CFClient::SDL_Init
1543     and die "SDL::Init failed!\n";
1544     }
1545    
1546     sub video_init {
1547     sdl_init;
1548    
1549     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1550    
1551     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1552    
1553     ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1554     $FULLSCREEN = $CFG->{fullscreen};
1555     $FAST = $CFG->{fast};
1556    
1557     CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
1558     or die "SDL_SetVideoMode failed: " . (CFClient::SDL_GetError) . "\n";
1559    
1560     $SDL_ACTIVE = 1;
1561     $LAST_REFRESH = time - 0.01;
1562    
1563 root 1.10 CFClient::OpenGL::init;
1564 root 1.1
1565     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1566    
1567     $CFClient::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1568    
1569     #############################################################################
1570    
1571     if ($DEBUG_STATUS) {
1572     CFClient::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1573     } else {
1574     # create the widgets
1575    
1576 root 1.30 $DEBUG_STATUS = new CFClient::UI::Label
1577     padding => 0,
1578     z => 100,
1579     force_x => "max",
1580     force_y => 0;
1581 root 1.1 $DEBUG_STATUS->show;
1582 elmex 1.34
1583 root 1.80 $BIND_EDITOR = new CFClient::BindingEditor (x => "max", y => 0);
1584 elmex 1.34
1585 root 1.1 $STATUSBOX = new CFClient::UI::Statusbox;
1586 root 1.54 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1587 root 1.1
1588     (new CFClient::UI::Frame
1589     bg => [0, 0, 0, 0.4],
1590 root 1.30 force_x => 0,
1591     force_y => "max",
1592 root 1.1 child => $STATUSBOX,
1593     )->show;
1594    
1595     CFClient::UI::FancyFrame->new (
1596 root 1.47 title => "Map",
1597 root 1.42 name => "mapmap",
1598 root 1.30 x => 0,
1599     y => $FONTSIZE + 8,
1600 root 1.1 border_bg => [1, 1, 1, 192/255],
1601     bg => [1, 1, 1, 0],
1602     child => ($MAPMAP = new CFClient::MapWidget::MapMap
1603     tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
1604     ),
1605     )->show;
1606    
1607     $MAPWIDGET = new CFClient::MapWidget;
1608     $MAPWIDGET->connect (activate_console => sub {
1609     my ($mapwidget, $preset) = @_;
1610    
1611     if ($CONSOLE) {
1612     $CONSOLE->{input}->{auto_activated} = 1;
1613 root 1.74 $CONSOLE->{input}->grab_focus;
1614 root 1.1
1615     if ($preset && $CONSOLE->{input}->get_text eq '') {
1616     $CONSOLE->{input}->set_text ($preset);
1617     }
1618     }
1619     });
1620     $MAPWIDGET->show;
1621 root 1.74 $MAPWIDGET->grab_focus;
1622 root 1.1
1623 root 1.64 $LOGVIEW = new CFClient::UI::TextScroller
1624 root 1.1 expand => 1,
1625     font => $FONT_FIXED,
1626     fontsize => $::CFG->{log_fontsize},
1627 root 1.61 indent => -4,
1628 root 1.1 can_hover => 1,
1629     can_events => 1,
1630     tooltip => "<b>Server Log</b>. This text viewer contains all the messages sent by the server.",
1631     ;
1632    
1633 root 1.49 $SETUP_DIALOG = new CFClient::UI::FancyFrame
1634     title => "Setup",
1635     name => "setup_dialog",
1636     x => 'center',
1637     y => 'center',
1638 root 1.53 z => 2,
1639 root 1.49 force_w => $::WIDTH * 0.6,
1640     force_h => $::HEIGHT * 0.6,
1641 root 1.74 has_close_button => 1,
1642 root 1.49 ;
1643    
1644 elmex 1.81 $METASERVER = metaserver_dialog;
1645    
1646 root 1.50 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFClient::UI::Notebook expand => 1, debug => 1,
1647 root 1.60 filter => new CFClient::UI::ScrolledWindow expand => 1, scroll_y => 1);
1648 root 1.49
1649     $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup,
1650     "Configure the server to play on, your username, password and other server-related options.");
1651     $SETUP_NOTEBOOK->add (Pickup => autopickup_setup,
1652 root 1.58 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1653 root 1.49 $SETUP_NOTEBOOK->add (Graphics => graphics_setup,
1654     "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1655     $SETUP_NOTEBOOK->add (Audio => audio_setup,
1656     "Configure the use of audio, sound effects and background music.");
1657     $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1658 root 1.75 "Lets you define, edit and delete key bindings."
1659     . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor "
1660 root 1.49 . "with nothing set and the recording started. After doing the actions you "
1661 root 1.54 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
1662 root 1.49 . "After pressing the combo the binding will be saved automatically and the "
1663     . "binding editor closes");
1664 root 1.65 $SETUP_NOTEBOOK->add (Debug => debug_setup,
1665 root 1.75 "Some debuggin' options. Do not ask.");
1666 root 1.49
1667 root 1.57 $BUTTONBAR = new CFClient::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1668 root 1.1
1669 root 1.49 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1670     tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1671    
1672 root 1.60 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW = message_window,
1673 root 1.1 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1674    
1675     make_gauge_window->show; # XXX: this has to be set before make_stats_window as make_stats_window calls update_stats_window which updated the gauges also X-D
1676    
1677 root 1.87 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Playerbook", other => player_window,
1678 elmex 1.85 tooltip => "Toggles the player view, where you can manage Inventory, Spells, Skills and see your Stats.");
1679 root 1.1
1680     $BUTTONBAR->add (new CFClient::UI::Button
1681     text => "Save Config",
1682     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
1683 root 1.18 on_activate => sub {
1684 elmex 1.16 $::CFG->{layout} = CFClient::UI::get_layout;
1685 root 1.28 CFClient::write_cfg "$Crossfire::VARDIR/cfplusrc";
1686 root 1.1 status "Configuration Saved";
1687 root 1.74 0
1688 root 1.1 },
1689     );
1690    
1691 root 1.86 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window,
1692 root 1.1 tooltip => "View Documentation");
1693    
1694     $BUTTONBAR->add (new CFClient::UI::Button
1695 root 1.18 text => "Quit",
1696     tooltip => "Terminates the program",
1697     on_activate => sub {
1698 root 1.1 if ($CONN) {
1699     open_quit_dialog;
1700     } else {
1701     exit;
1702     }
1703 root 1.74 0
1704 root 1.1 },
1705     );
1706    
1707     $BUTTONBAR->show;
1708 root 1.49 $SETUP_DIALOG->show;
1709     }
1710 root 1.1
1711 root 1.49 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1712 root 1.1 }
1713    
1714     sub video_shutdown {
1715 root 1.73 CFClient::OpenGL::shutdown;
1716    
1717 root 1.1 undef $SDL_ACTIVE;
1718     }
1719    
1720     my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
1721     my $bgmusic;#TODO#hack#d#
1722    
1723     sub audio_channel_finished {
1724     my ($channel) = @_;
1725    
1726     #warn "channel $channel finished\n";#d#
1727     }
1728    
1729     sub audio_music_finished {
1730     return unless $CFG->{bgm_enable};
1731    
1732     # TODO: hack, do play loop and mood music
1733     $bgmusic = new_from_file CFClient::MixMusic CFClient::find_rcfile "music/$bgmusic[0]";
1734     $bgmusic->play (0);
1735    
1736     push @bgmusic, shift @bgmusic;
1737     }
1738    
1739     sub audio_init {
1740     if ($CFG->{audio_enable}) {
1741     if (open my $fh, "<", CFClient::find_rcfile "sounds/config") {
1742     $SDL_MIXER = !CFClient::Mix_OpenAudio;
1743    
1744     unless ($SDL_MIXER) {
1745     status "Unable to open sound device: there will be no sound";
1746     return;
1747     }
1748    
1749     CFClient::Mix_AllocateChannels 8;
1750     CFClient::MixMusic::volume $CFG->{bgm_volume} * 128;
1751    
1752     audio_music_finished;
1753    
1754     while (<$fh>) {
1755     next if /^\s*#/;
1756     next if /^\s*$/;
1757    
1758     my ($file, $volume, $event) = split /\s+/, $_, 3;
1759    
1760     push @SOUNDS, "$volume,$file";
1761    
1762     $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1763     my $chunk = new_from_file CFClient::MixChunk CFClient::find_rcfile "sounds/$file";
1764     $chunk->volume ($volume * 128 / 100);
1765     $chunk
1766     };
1767     }
1768     } else {
1769     status "unable to open sound config: $!";
1770     }
1771     }
1772     }
1773    
1774     sub audio_shutdown {
1775     CFClient::Mix_CloseAudio if $SDL_MIXER;
1776     undef $SDL_MIXER;
1777     @SOUNDS = ();
1778     %AUDIO_CHUNKS = ();
1779     }
1780    
1781     my %animate_object;
1782     my $animate_timer;
1783    
1784     my $fps = 9;
1785    
1786     my %demo;#d#
1787    
1788     sub force_refresh {
1789     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1790 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1791 root 1.1
1792     $CFClient::UI::ROOT->draw;
1793    
1794     $WANT_REFRESH = 0;
1795     $CAN_REFRESH = 0;
1796     $LAST_REFRESH = $NOW;
1797    
1798     CFClient::SDL_GL_SwapBuffers;
1799     }
1800    
1801 root 1.49 my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1802 root 1.1 $NOW = time;
1803    
1804     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1805     for CFClient::SDL_PollEvent;
1806    
1807     if (%animate_object) {
1808     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1809     $WANT_REFRESH++;
1810     }
1811    
1812     if ($WANT_REFRESH) {
1813     force_refresh;
1814     } else {
1815     $CAN_REFRESH = 1;
1816     }
1817     });
1818    
1819     sub animation_start {
1820     my ($widget) = @_;
1821     $animate_object{$widget} = $widget;
1822     }
1823    
1824     sub animation_stop {
1825     my ($widget) = @_;
1826     delete $animate_object{$widget};
1827     }
1828    
1829     # check once/second for faces that need to be prefetched
1830     # this should, of course, only run on demand, but
1831     # SDL forces worse things on us....
1832    
1833     Event->timer (after => 1, interval => 0.25, cb => sub {
1834     $CONN->face_prefetch
1835     if $CONN;
1836     });
1837    
1838     %SDL_CB = (
1839     CFClient::SDL_QUIT => sub {
1840     Event::unloop -1;
1841     },
1842     CFClient::SDL_VIDEORESIZE => sub {
1843     },
1844     CFClient::SDL_VIDEOEXPOSE => sub {
1845     CFClient::UI::full_refresh;
1846     },
1847     CFClient::SDL_ACTIVEEVENT => sub {
1848     # printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d#
1849     },
1850     CFClient::SDL_KEYDOWN => sub {
1851     if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) {
1852     # alt-enter
1853 root 1.94 $FULLSCREEN_ENABLE->toggle;
1854 root 1.1 video_shutdown;
1855     video_init;
1856     } else {
1857     CFClient::UI::feed_sdl_key_down_event ($_[0]);
1858     }
1859     },
1860     CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event,
1861     CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event,
1862     CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event,
1863     CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event,
1864     CFClient::SDL_USEREVENT => sub {
1865     if ($_[0]{code} == 1) {
1866     audio_channel_finished $_[0]{data1};
1867     } elsif ($_[0]{code} == 0) {
1868     audio_music_finished;
1869     }
1870     },
1871     );
1872    
1873     #############################################################################
1874    
1875     $SIG{INT} = $SIG{TERM} = sub { exit };
1876    
1877     {
1878 root 1.28 CFClient::read_cfg "$Crossfire::VARDIR/cfplusrc";
1879 elmex 1.16 CFClient::UI::set_layout ($::CFG->{layout});
1880 root 1.1
1881     my %DEF_CFG = (
1882 root 1.75 sdl_mode => 0,
1883     width => 640,
1884     height => 480,
1885     fullscreen => 0,
1886     fast => 0,
1887     map_scale => 1,
1888     fow_enable => 1,
1889     fow_intensity => 0.45,
1890     fow_smooth => 0,
1891     gui_fontsize => 1,
1892     log_fontsize => 0.7,
1893     gauge_fontsize => 1,
1894     gauge_size => 0.35,
1895     stat_fontsize => 0.7,
1896     mapsize => 100,
1897     say_command => 'say',
1898     audio_enable => 1,
1899     bgm_enable => 1,
1900     bgm_volume => 0.25,
1901     face_prefetch => 0,
1902     output_sync => 1,
1903     output_count => 1,
1904     pickup => 0,
1905 root 1.102 inv_sort => "mtime",
1906 root 1.75 default => "profile", # default profile
1907 root 1.1 );
1908 root 1.75
1909 root 1.1 while (my ($k, $v) = each %DEF_CFG) {
1910     $CFG->{$k} = $v unless exists $CFG->{$k};
1911     }
1912    
1913 root 1.75 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
1914    
1915 root 1.1 sdl_init;
1916    
1917     @SDL_MODES = reverse
1918     grep $_->[0] >= 640 && $_->[1] >= 480,
1919     CFClient::SDL_ListModes;
1920    
1921     @SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
1922    
1923     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
1924    
1925     {
1926     my @fonts = map CFClient::find_rcfile "fonts/$_", qw(
1927     DejaVuSans.ttf
1928     DejaVuSansMono.ttf
1929     DejaVuSans-Bold.ttf
1930     DejaVuSansMono-Bold.ttf
1931     DejaVuSans-Oblique.ttf
1932     DejaVuSansMono-Oblique.ttf
1933     DejaVuSans-BoldOblique.ttf
1934     DejaVuSansMono-BoldOblique.ttf
1935     );
1936    
1937     CFClient::add_font $_ for @fonts;
1938    
1939     CFClient::pango_init;
1940    
1941     $FONT_PROP = new_from_file CFClient::Font $fonts[0];
1942     $FONT_FIXED = new_from_file CFClient::Font $fonts[1];
1943    
1944     $FONT_PROP->make_default;
1945     }
1946    
1947     # compare mono (ft) vs. rgba (cairo)
1948     # ft - 1.8s, cairo 3s, even in alpha-only mode
1949     # for my $rgba (0..1) {
1950     # my $t1 = Time::HiRes::time;
1951     # for (1..1000) {
1952     # my $layout = CFClient::Layout->new ($rgba);
1953     # $layout->set_text ("hallo" x 100);
1954     # $layout->render;
1955     # }
1956     # my $t2 = Time::HiRes::time;
1957     # warn $t2-$t1;
1958     # }
1959    
1960     video_init;
1961     audio_init;
1962     }
1963    
1964     Event::loop;
1965 root 1.69 #CFClient::SDL_Quit;
1966     #CFClient::_exit 0;
1967 root 1.1
1968     END { CFClient::SDL_Quit }
1969    
1970     =head1 NAME
1971    
1972 root 1.28 cfplus - A Crossfire+ and Crossfire game client
1973 root 1.1
1974     =head1 SYNOPSIS
1975    
1976     Just run it - no commandline arguments are supported.
1977    
1978     =head1 USAGE
1979    
1980 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
1981 root 1.1 fullscreen and interactively.
1982    
1983 root 1.39 =head1 DEBUGGING
1984    
1985    
1986     CFPLUS_DEBUG - environment variable
1987    
1988     1 draw borders around widgets
1989     2 add low-level widget info to tooltips
1990     4 show fps
1991     8 suppress tooltips
1992    
1993 root 1.1 =head1 AUTHOR
1994    
1995     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
1996    
1997    
1998