ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.143
Committed: Fri Mar 16 02:33:50 2007 UTC (17 years, 2 months ago) by root
Branch: MAIN
Changes since 1.142: +18 -8 lines
Log Message:
implement output-rate setting

File Contents

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