ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.146
Committed: Wed Apr 4 02:43:30 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.145: +1 -1 lines
Log Message:
support new tileset negotiation

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