ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.189
Committed: Sun Jul 29 18:22:34 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.188: +7 -30 lines
Log Message:
rid us of the pest of face prefetching

File Contents

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