ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.187
Committed: Fri Jul 27 18:04:43 2007 UTC (16 years, 10 months ago) by elmex
Branch: MAIN
Changes since 1.186: +0 -122 lines
Log Message:
removed build support

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.178 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Face Prefetch");
1127     $table->add_at (1, 8, new CFPlus::UI::CheckBox
1128 root 1.1 state => $CFG->{face_prefetch},
1129     tooltip => "<b>Background Image Prefetch</b>\n\n"
1130     . "If enabled, the client automatically pre-fetches images from the server. "
1131     . "This might increase or create lag, but increases the chances "
1132     . "of faces being ready for display when you encounter them. "
1133     . "It also uses up server bandwidth on every connect, "
1134     . "so only set it if you really need to prefetch images. "
1135     . "This option can be set and unset any time.",
1136 root 1.74 on_changed => sub { $CFG->{face_prefetch} = $_[1]; 0 },
1137 root 1.1 );
1138    
1139 root 1.178 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate");
1140     $table->add_at (1, 9, new CFPlus::UI::Entry
1141 root 1.143 text => $CFG->{output_rate},
1142     tooltip => "The approximate bandwidth in bytes per second that the server should not exceed "
1143     . "when sending images, to ensure interactiveness. When 0 or unset, the server "
1144     . "default will be used, which is usually around 100kb/s.",
1145     on_changed => sub { $CFG->{output_rate} = $_[1]; 0 },
1146     );
1147    
1148 root 1.178 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1149     $table->add_at (1, 10, new CFPlus::UI::Entry
1150 root 1.1 text => $CFG->{output_count},
1151     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1152 root 1.74 on_changed => sub { $CFG->{output_count} = $_[1]; 0 },
1153 root 1.1 );
1154    
1155 root 1.178 $table->add_at (0, 11, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync");
1156     $table->add_at (1, 11, new CFPlus::UI::Entry
1157 root 1.1 text => $CFG->{output_sync},
1158     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1159 root 1.74 on_changed => sub { $CFG->{output_sync} = $_[1]; 0 },
1160 root 1.1 );
1161    
1162 root 1.178 $table->add_at (1, 12, $LOGIN_BUTTON = new CFPlus::UI::Button
1163 root 1.1 expand => 1,
1164     align => 0,
1165     text => "Login",
1166 root 1.18 on_activate => sub {
1167 root 1.1 $CONN ? stop_game
1168     : start_game;
1169 root 1.74 0
1170 root 1.1 },
1171     );
1172    
1173 root 1.149 $vbox->add (new CFPlus::UI::FancyFrame
1174     label => "Server Info",
1175     child => ($SERVER_INFO = new CFPlus::UI::Label ellipsise => 0),
1176     );
1177    
1178     $vbox
1179     }
1180    
1181     sub client_setup {
1182     my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1];
1183    
1184     my $row = 0;
1185    
1186 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day");
1187     $table->add_at (1, $row++, new CFPlus::UI::CheckBox
1188 root 1.119 state => $CFG->{show_tips},
1189     tooltip => "Show the <b>Tip of the day</b> window at startup?",
1190     on_changed => sub {
1191     my ($self, $value) = @_;
1192 root 1.121 $CFG->{show_tips} = $value;
1193 root 1.119 0
1194     }
1195     );
1196    
1197 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Messages Window Size");
1198     $table->add_at (1, $row++, my $saycmd = new CFPlus::UI::Entry
1199 root 1.149 text => $CFG->{logview_max_par},
1200     tooltip => "This is maximum number of messages remembered in the <b>Messages</b> window. If the server "
1201     . "sends more messages than this number, older messages get removed to save memory and "
1202     . "computing time. A value of <b>0</b> disables this feature, but that is not recommended.",
1203     on_changed => sub {
1204     my ($self, $value) = @_;
1205 elmex 1.182 $MESSAGE_WINDOW->set_max_para ($CFG->{logview_max_par} = $value*1);
1206 root 1.149 0
1207     },
1208 root 1.67 );
1209    
1210 root 1.149 $table
1211 root 1.1 }
1212    
1213 root 1.49 sub autopickup_setup {
1214 root 1.170 my $r = new CFPlus::UI::ScrolledWindow (
1215     expand => 1,
1216     scroll_y => 1
1217     );
1218     $r->add (my $table = new CFPlus::UI::Table
1219     row_expand => [0],
1220     col_expand => [0, 1, 0, 1],
1221     );
1222 elmex 1.44
1223 elmex 1.43 for (
1224 root 1.51 ["General", 0, 0,
1225 root 1.86 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE],
1226 root 1.58 ["Inhibit autopickup" => PICKUP_INHIBIT],
1227     ["Stop before pickup" => PICKUP_STOP],
1228     ["Debug autopickup" => PICKUP_DEBUG],
1229 root 1.51 ],
1230     ["Weapons", 0, 6,
1231 root 1.58 ["All weapons" => PICKUP_ALLWEAPON],
1232     ["Missile weapons" => PICKUP_MISSILEWEAPON],
1233     ["Bows" => PICKUP_BOW],
1234     ["Arrows" => PICKUP_ARROW],
1235 root 1.51 ],
1236     ["Armour", 0, 12,
1237 root 1.58 ["Helmets" => PICKUP_HELMET],
1238     ["Shields" => PICKUP_SHIELD],
1239     ["Body Armour" => PICKUP_ARMOUR],
1240     ["Boots" => PICKUP_BOOTS],
1241     ["Gloves" => PICKUP_GLOVES],
1242     ["Cloaks" => PICKUP_CLOAK],
1243 root 1.51 ],
1244    
1245 root 1.133 ["Readables", 2, 0,
1246 root 1.58 ["Spellbooks" => PICKUP_SPELLBOOK],
1247     ["Skillscrolls" => PICKUP_SKILLSCROLL],
1248     ["Normal Books/Scrolls" => PICKUP_READABLES],
1249 root 1.51 ],
1250 root 1.133 ["Misc", 2, 5,
1251 root 1.58 ["Food" => PICKUP_FOOD],
1252     ["Drinks" => PICKUP_DRINK],
1253     ["Valuables (Money, Gems)" => PICKUP_VALUABLES],
1254     ["Keys" => PICKUP_KEY],
1255     ["Magical Items" => PICKUP_MAGICAL],
1256     ["Potions" => PICKUP_POTION],
1257     ["Magic Devices" => PICKUP_MAGIC_DEVICE],
1258     ["Ignore cursed" => PICKUP_NOT_CURSED],
1259     ["Jewelery" => PICKUP_JEWELS],
1260 root 1.133 ["Flesh" => PICKUP_FLESH],
1261 root 1.51 ],
1262 elmex 1.66 ["Weight/Value ratio", 2, 17]
1263 elmex 1.43 )
1264     {
1265 root 1.51 my ($title, $x, $y, @bits) = @$_;
1266 root 1.178 $table->add_at ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
1267 root 1.51
1268     for (@bits) {
1269     ++$y;
1270    
1271 elmex 1.43 my $mask = $_->[1];
1272 root 1.178 $table->add_at ($x , $y, new CFPlus::UI::Label text => $_->[0], align => 1, expand => 1);
1273     $table->add_at ($x+1, $y, my $checkbox = new CFPlus::UI::CheckBox
1274 elmex 1.83 state => $::CFG->{pickup} & $mask,
1275 elmex 1.43 on_changed => sub {
1276     my ($box, $value) = @_;
1277 root 1.63
1278 elmex 1.43 if ($value) {
1279 elmex 1.45 $::CFG->{pickup} |= $mask;
1280 elmex 1.43 } else {
1281 root 1.63 $::CFG->{pickup} &= ~$mask;
1282 elmex 1.43 }
1283 root 1.63
1284     $::CONN->send_command ("pickup $::CFG->{pickup}")
1285 elmex 1.45 if defined $::CONN;
1286 root 1.74
1287     0
1288 elmex 1.43 });
1289 root 1.86
1290     ${$_->[2]} = $checkbox if $_->[2];
1291 elmex 1.43 }
1292     }
1293    
1294 root 1.178 $table->add_at (2, 18, new CFPlus::UI::ValSlider
1295 elmex 1.83 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1],
1296     template => ">= 99",
1297 elmex 1.66 to_value => sub { ">= " . 5 * $_[0] },
1298     on_changed => sub {
1299     my ($slider, $value) = @_;
1300    
1301 elmex 1.83 $::CFG->{pickup} &= ~0xF;
1302 elmex 1.66 $::CFG->{pickup} |= int $value
1303     if $value;
1304     1;
1305     });
1306 elmex 1.83
1307 root 1.178 $table->add_at (3, 18, new CFPlus::UI::Button
1308 elmex 1.66 text => "set",
1309     on_activate => sub {
1310     $::CONN->send_command ("pickup $::CFG->{pickup}")
1311     if defined $::CONN;
1312 root 1.74 0
1313 elmex 1.66 });
1314    
1315 root 1.170 $r
1316 elmex 1.43 }
1317    
1318 root 1.102 my %SORT_ORDER = (
1319     type => undef,
1320 root 1.130 mtime => sub {
1321     my $NOW = time;
1322     sort {
1323     my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6;
1324     my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6;
1325    
1326     ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1327     or $btime <=> $atime
1328     or $a->{type} <=> $b->{type}
1329     } @_
1330     },
1331 root 1.102 weight => sub { sort {
1332     $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1333     or $a->{type} <=> $b->{type}
1334     } @_ },
1335     );
1336    
1337 elmex 1.85 sub inventory_widget {
1338 root 1.104 my $hb = new CFPlus::UI::HBox homogeneous => 1;
1339 root 1.1
1340 root 1.104 $hb->add (my $vb1 = new CFPlus::UI::VBox);
1341     $vb1->add (new CFPlus::UI::Label align => 0, text => "Player");
1342 root 1.99
1343 root 1.104 $vb1->add (my $hb1 = new CFPlus::UI::HBox);
1344 root 1.99
1345     use sort 'stable';
1346    
1347 root 1.104 $hb1->add (new CFPlus::UI::Selector
1348 root 1.102 value => $::CFG->{inv_sort},
1349 root 1.99 options => [
1350 root 1.102 [type => "Type/Name"],
1351     [mtime => "Recent/Normal/Locked"],
1352     [weight => "Weight/Type"],
1353 root 1.99 ],
1354     on_changed => sub {
1355 root 1.102 $::CFG->{inv_sort} = $_[1];
1356     $INV->set_sort_order ($SORT_ORDER{$_[1]});
1357 root 1.99 },
1358     );
1359 root 1.104 $hb1->add (new CFPlus::UI::Label text => "Weight: ", align => 1, expand => 1);
1360 root 1.99 #TODO# update to weigh/maxweight
1361 root 1.104 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1);
1362 root 1.99
1363 root 1.104 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1364     $sw1->add ($INV = new CFPlus::UI::Inventory);
1365 root 1.127 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}});
1366 root 1.1
1367 root 1.104 $hb->add (my $vb2 = new CFPlus::UI::VBox);
1368 elmex 1.17
1369 root 1.104 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox);
1370 elmex 1.14
1371 root 1.104 $vb2->add (my $sw2 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1372     $sw2->add ($INVR = new CFPlus::UI::Inventory);
1373 root 1.1
1374 elmex 1.27 # XXX: Call after $INVR = ... because set_opencont sets the items
1375 root 1.104 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor");
1376 elmex 1.27
1377 elmex 1.85 $hb
1378 root 1.1 }
1379    
1380 root 1.86 sub toggle_player_page {
1381     my ($widget) = @_;
1382    
1383     if ($PL_WINDOW->{visible} && $PL_NOTEBOOK->get_current_page == $widget) {
1384     $PL_WINDOW->hide;
1385     } else {
1386     $PL_NOTEBOOK->set_current_page ($widget);
1387     $PL_WINDOW->show;
1388     }
1389     }
1390    
1391 elmex 1.85 sub player_window {
1392 root 1.114 my $plwin = $PL_WINDOW = new CFPlus::UI::Toplevel
1393 elmex 1.85 x => "center",
1394     y => "center",
1395     force_w => $WIDTH * 9/10,
1396     force_h => $HEIGHT * 9/10,
1397     title => "Player",
1398 elmex 1.90 name => "playerbook",
1399 elmex 1.85 has_close_button => 1
1400     ;
1401    
1402     my $ntb =
1403     $PL_NOTEBOOK =
1404 root 1.157 new CFPlus::UI::Notebook expand => 1;
1405 root 1.86
1406 root 1.184 $ntb->add_tab (
1407 root 1.95 "Statistics (F2)" => $STATS_PAGE = stats_window,
1408 elmex 1.92 "Shows statistics, where all your Stats and Resistances are shown."
1409     );
1410 root 1.184 $ntb->add_tab (
1411 root 1.95 "Skills (F3)" => $SKILL_PAGE = skill_window,
1412 elmex 1.92 "Shows all your Skills."
1413 elmex 1.85 );
1414 elmex 1.97
1415 root 1.131 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1416     $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList);
1417 root 1.184 $ntb->add_tab (
1418 elmex 1.97 "Spellbook (F4)" => $spellsw,
1419 root 1.86 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1420 elmex 1.85 );
1421 root 1.184 $ntb->add_tab (
1422 root 1.95 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1423 root 1.86 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1424     . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1425 elmex 1.85 );
1426 root 1.184 $ntb->add_tab (Pickup => autopickup_setup,
1427 root 1.155 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1428 elmex 1.85
1429 root 1.88 $ntb->set_current_page ($INVENTORY_PAGE);
1430 root 1.86
1431 elmex 1.85 $plwin->add ($ntb);
1432     $plwin
1433 elmex 1.38 }
1434    
1435 root 1.49 sub keyboard_setup {
1436 root 1.137 CFPlus::Macro::keyboard_setup
1437 elmex 1.24 }
1438    
1439 root 1.64 sub help_window {
1440 root 1.114 my $win = new CFPlus::UI::Toplevel
1441 root 1.41 x => 'center',
1442     y => 'center',
1443 root 1.119 z => 4,
1444 root 1.41 name => 'doc_browser',
1445     force_w => int $WIDTH * 7/8,
1446     force_h => int $HEIGHT * 7/8,
1447 root 1.87 title => "Help Browser",
1448     has_close_button => 1;
1449 root 1.1
1450 root 1.104 $win->add (my $vbox = new CFPlus::UI::VBox);
1451 root 1.1
1452 root 1.114 $vbox->add (new CFPlus::UI::FancyFrame
1453     label => "Navigation",
1454     child => (my $buttons = new CFPlus::UI::HBox),
1455     );
1456 root 1.104 $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1457 root 1.114 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1458 root 1.1
1459 root 1.111 my @history;
1460     my @future;
1461 root 1.112 my $curnode;
1462 root 1.111
1463     my $load_node; $load_node = sub {
1464 root 1.115 my ($node, $para) = @_;
1465 root 1.111
1466     $buttons->clear;
1467    
1468 root 1.116 $buttons->add (new CFPlus::UI::Button
1469     text => "⇤",
1470     tooltip => "back to the starting page",
1471     on_activate => sub {
1472     unshift @future, [$curnode, $viewer->current_paragraph] if $curnode;
1473     unshift @future, @history;
1474     @history = ();
1475     $load_node->(@{shift @future});
1476     },
1477     );
1478    
1479 root 1.111 if (@history) {
1480 root 1.112 $buttons->add (new CFPlus::UI::Button
1481 root 1.113 text => "⋘",
1482 root 1.116 tooltip => "back to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $history[-1][0]) . "</i>",
1483 root 1.112 on_activate => sub {
1484 root 1.115 unshift @future, [$curnode, $viewer->current_paragraph] if $curnode;
1485     $load_node->(@{pop @history});
1486 root 1.113 },
1487     );
1488 root 1.112 }
1489 root 1.111
1490 root 1.112 if (@future) {
1491     $buttons->add (new CFPlus::UI::Button
1492 root 1.113 text => "â‹™",
1493 root 1.116 tooltip => "forward to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $future[0][0]) . "</i>",
1494 root 1.112 on_activate => sub {
1495 root 1.115 push @history, [$curnode, $viewer->current_paragraph];
1496     $load_node->(@{shift @future});
1497 root 1.113 },
1498     );
1499 root 1.111 }
1500 root 1.112
1501 root 1.113 $buttons->add (new CFPlus::UI::Label text => " ");
1502    
1503     my @path = CFPlus::Pod::full_path_of $node;
1504     pop @path; # drop current node
1505    
1506     for my $node (@path) {
1507     $buttons->add (new CFPlus::UI::Button
1508     text => $node->{kw}[0],
1509     tooltip => "go to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $node) . "</i>",
1510     on_activate => sub {
1511 root 1.115 push @history, [$curnode, $viewer->current_paragraph] if $curnode; @future = ();
1512 root 1.113 $load_node->($node);
1513     },
1514     );
1515     $buttons->add (new CFPlus::UI::Label text => "/");
1516     }
1517    
1518 root 1.114 $buttons->add (new CFPlus::UI::Label text => $node->{kw}[0], padding_x => 4, padding_y => 4);
1519 root 1.113
1520 root 1.112 $curnode = $node;
1521    
1522 root 1.111 $viewer->clear;
1523 root 1.112 $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $curnode);
1524 root 1.115 $viewer->scroll_to ($para);
1525 root 1.111 };
1526    
1527     $load_node->(CFPlus::Pod::find pod => "mainpage");
1528    
1529 root 1.113 $CFPlus::Pod::goto_document = sub {
1530 root 1.111 my (@path) = @_;
1531    
1532 root 1.115 push @history, [$curnode, $viewer->current_paragraph] if $curnode; @future = ();
1533 root 1.111
1534 root 1.116 $load_node->((CFPlus::Pod::find @path)[0]);
1535 root 1.111 $win->show;
1536     };
1537 root 1.1
1538     $win
1539     }
1540    
1541 root 1.119 sub open_string_query {
1542     my ($title, $cb, $txt, $tooltip) = @_;
1543     my $dialog = new CFPlus::UI::Toplevel
1544     x => "center",
1545     y => "center",
1546     z => 50,
1547     force_w => $WIDTH * 4/5,
1548     title => $title;
1549    
1550     $dialog->add (
1551     my $e = new CFPlus::UI::Entry
1552     on_activate => sub { $cb->(@_); $dialog->hide; 0 },
1553     on_key_down => sub { $_[1]->{sym} == 27 and $dialog->hide; 0 },
1554     tooltip => $tooltip
1555     );
1556    
1557     $e->grab_focus;
1558     $e->set_text ($txt) if $txt;
1559     $dialog->show;
1560     }
1561    
1562     sub open_quit_dialog {
1563     unless ($QUIT_DIALOG) {
1564     $QUIT_DIALOG = new CFPlus::UI::Toplevel
1565     x => "center",
1566     y => "center",
1567     z => 50,
1568     title => "Really Quit?",
1569     on_key_down => sub {
1570     my ($dialog, $ev) = @_;
1571     $ev->{sym} == 27 and $dialog->hide;
1572     }
1573     ;
1574    
1575     $QUIT_DIALOG->add (my $vb = new CFPlus::UI::VBox expand => 1);
1576    
1577     $vb->add (new CFPlus::UI::Label
1578     text => "You should find a savebed and apply it first!",
1579     max_w => $WIDTH * 0.25,
1580     ellipsize => 0,
1581     );
1582     $vb->add (my $hb = new CFPlus::UI::HBox expand => 1);
1583     $hb->add (new CFPlus::UI::Button
1584     text => "Ok",
1585     expand => 1,
1586     on_activate => sub { $QUIT_DIALOG->hide; 0 },
1587     );
1588     $hb->add (new CFPlus::UI::Button
1589     text => "Quit anyway",
1590     expand => 1,
1591     on_activate => sub { exit },
1592     );
1593     }
1594    
1595     $QUIT_DIALOG->show;
1596     $QUIT_DIALOG->grab_focus;
1597     }
1598    
1599     sub show_tip_of_the_day {
1600     # find all tips
1601     my @tod = CFPlus::Pod::find tip_of_the_day => "*";
1602    
1603 root 1.148 CFPlus::DB::get state => "tip_of_the_day", sub {
1604     my ($todindex) = @_;
1605     $todindex = 0 if $todindex >= @tod;
1606     CFPlus::DB::put state => tip_of_the_day => $todindex + 1, sub { };
1607    
1608     # create dialog
1609     my $dialog;
1610    
1611     my $close = sub {
1612     $dialog->destroy;
1613     };
1614    
1615     $dialog = new CFPlus::UI::Toplevel
1616     x => "center",
1617     y => "center",
1618     z => 3,
1619     name => 'tip_of_the_day',
1620     force_w => int $WIDTH * 4/9,
1621     force_h => int $WIDTH * 2/9,
1622     title => "Tip of the day #" . (1 + $todindex),
1623     child => my $vbox = new CFPlus::UI::VBox,
1624     has_close_button => 1,
1625     on_delete => $close,
1626     ;
1627    
1628     $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1629     expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1630     $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $tod[$todindex]);
1631    
1632     $vbox->add (my $table = new CFPlus::UI::Table col_expand => [0, 1]);
1633    
1634 root 1.178 $table->add_at (0, 0, new CFPlus::UI::Button
1635 root 1.148 text => "Close",
1636     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>.",
1637     on_activate => $close,
1638     );
1639 root 1.119
1640 root 1.178 $table->add_at (2, 0, new CFPlus::UI::Button
1641 root 1.148 text => "Next",
1642     tooltip => "Show the next <b>Tip of the day</b>.",
1643     on_activate => sub {
1644     $close->();
1645     &show_tip_of_the_day;
1646     },
1647     );
1648 root 1.119
1649 root 1.148 $dialog->show;
1650 root 1.119 };
1651     }
1652    
1653 root 1.1 sub sdl_init {
1654 root 1.104 CFPlus::SDL_Init
1655 root 1.1 and die "SDL::Init failed!\n";
1656     }
1657    
1658     sub video_init {
1659     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1660    
1661     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1662    
1663 root 1.172 ($WIDTH, $HEIGHT, my ($rgb, $alpha)) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1664 root 1.1 $FULLSCREEN = $CFG->{fullscreen};
1665     $FAST = $CFG->{fast};
1666    
1667 root 1.172 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $rgb, $alpha, $FULLSCREEN
1668 root 1.104 or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n";
1669 root 1.1
1670     $SDL_ACTIVE = 1;
1671     $LAST_REFRESH = time - 0.01;
1672    
1673 root 1.104 CFPlus::OpenGL::init;
1674 root 1.1
1675     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1676    
1677 root 1.104 $CFPlus::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1678 root 1.1
1679     #############################################################################
1680    
1681     if ($DEBUG_STATUS) {
1682 root 1.104 CFPlus::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1683 root 1.1 } else {
1684     # create the widgets
1685    
1686 root 1.104 $DEBUG_STATUS = new CFPlus::UI::Label
1687 root 1.30 padding => 0,
1688     z => 100,
1689     force_x => "max",
1690     force_y => 0;
1691 root 1.1 $DEBUG_STATUS->show;
1692 elmex 1.34
1693 root 1.104 $STATUSBOX = new CFPlus::UI::Statusbox;
1694 root 1.54 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1695 root 1.1
1696 root 1.104 (new CFPlus::UI::Frame
1697 root 1.1 bg => [0, 0, 0, 0.4],
1698 root 1.30 force_x => 0,
1699     force_y => "max",
1700 root 1.1 child => $STATUSBOX,
1701     )->show;
1702    
1703 root 1.114 CFPlus::UI::Toplevel->new (
1704 root 1.47 title => "Map",
1705 root 1.42 name => "mapmap",
1706 root 1.30 x => 0,
1707     y => $FONTSIZE + 8,
1708 root 1.1 border_bg => [1, 1, 1, 192/255],
1709     bg => [1, 1, 1, 0],
1710 root 1.104 child => ($MAPMAP = new CFPlus::MapWidget::MapMap
1711 root 1.1 tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
1712     ),
1713     )->show;
1714    
1715 root 1.104 $MAPWIDGET = new CFPlus::MapWidget;
1716 root 1.1 $MAPWIDGET->connect (activate_console => sub {
1717     my ($mapwidget, $preset) = @_;
1718    
1719 elmex 1.182 $MESSAGE_WINDOW->activate_console ($preset)
1720     if $MESSAGE_WINDOW;
1721 root 1.1 });
1722     $MAPWIDGET->show;
1723 root 1.74 $MAPWIDGET->grab_focus;
1724 root 1.1
1725 root 1.114 $SETUP_DIALOG = new CFPlus::UI::Toplevel
1726 root 1.49 title => "Setup",
1727     name => "setup_dialog",
1728     x => 'center',
1729     y => 'center',
1730 root 1.53 z => 2,
1731 root 1.49 force_w => $::WIDTH * 0.6,
1732     force_h => $::HEIGHT * 0.6,
1733 root 1.74 has_close_button => 1,
1734 root 1.49 ;
1735    
1736 elmex 1.81 $METASERVER = metaserver_dialog;
1737 elmex 1.182 $MESSAGE_WINDOW = new CFPlus::UI::MessageWindow;
1738 elmex 1.81
1739 root 1.104 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1,
1740     filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1741 root 1.49
1742 root 1.184 $SETUP_NOTEBOOK->add_tab (Server => $SETUP_SERVER = server_setup,
1743 root 1.49 "Configure the server to play on, your username, password and other server-related options.");
1744 root 1.184 $SETUP_NOTEBOOK->add_tab (Client => client_setup,
1745 root 1.149 "Configure various client-specific settings.");
1746 root 1.184 $SETUP_NOTEBOOK->add_tab (Graphics => graphics_setup,
1747 root 1.49 "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1748 root 1.184 $SETUP_NOTEBOOK->add_tab (Audio => audio_setup,
1749 root 1.49 "Configure the use of audio, sound effects and background music.");
1750 root 1.184 $SETUP_NOTEBOOK->add_tab (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1751 root 1.75 "Lets you define, edit and delete key bindings."
1752     . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor "
1753 root 1.49 . "with nothing set and the recording started. After doing the actions you "
1754 root 1.54 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
1755 root 1.49 . "After pressing the combo the binding will be saved automatically and the "
1756     . "binding editor closes");
1757 root 1.184 $SETUP_NOTEBOOK->add_tab (Debug => debug_setup,
1758 root 1.75 "Some debuggin' options. Do not ask.");
1759 root 1.49
1760 root 1.104 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1761 root 1.1
1762 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1763 root 1.49 tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1764    
1765 elmex 1.182 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW,
1766 root 1.1 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1767    
1768     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
1769    
1770 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window,
1771 elmex 1.85 tooltip => "Toggles the player view, where you can manage Inventory, Spells, Skills and see your Stats.");
1772 root 1.1
1773 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Button
1774 root 1.1 text => "Save Config",
1775     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
1776 root 1.18 on_activate => sub {
1777 root 1.104 $::CFG->{layout} = CFPlus::UI::get_layout;
1778     CFPlus::write_cfg "$Crossfire::VARDIR/cfplusrc";
1779 root 1.1 status "Configuration Saved";
1780 root 1.74 0
1781 root 1.1 },
1782     );
1783    
1784 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window,
1785 root 1.1 tooltip => "View Documentation");
1786    
1787 elmex 1.134
1788 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Button
1789 root 1.18 text => "Quit",
1790     tooltip => "Terminates the program",
1791     on_activate => sub {
1792 root 1.1 if ($CONN) {
1793     open_quit_dialog;
1794     } else {
1795     exit;
1796     }
1797 root 1.74 0
1798 root 1.1 },
1799     );
1800    
1801     $BUTTONBAR->show;
1802 root 1.49 $SETUP_DIALOG->show;
1803     }
1804 root 1.1
1805 root 1.49 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1806 root 1.1 }
1807    
1808     sub video_shutdown {
1809 root 1.104 CFPlus::OpenGL::shutdown;
1810 root 1.73
1811 root 1.1 undef $SDL_ACTIVE;
1812     }
1813    
1814     sub audio_channel_finished {
1815     my ($channel) = @_;
1816    
1817     #warn "channel $channel finished\n";#d#
1818     }
1819    
1820 root 1.163 sub audio_music_set {
1821     my ($songs) = @_;
1822    
1823 root 1.168 my @want =
1824     grep $_,
1825     map $CONN->{music_meta}{$_},
1826     @$songs;
1827 root 1.163
1828 root 1.168 if (@want) {
1829     @MUSIC_WANT = @want;
1830     &audio_music_changed ();
1831     }
1832     }
1833    
1834     sub audio_music_start {
1835     my $path = $MUSIC_PLAYING->{path}
1836     or return;
1837    
1838     CFPlus::DB::prefetch_file $path, 1024_000, sub {
1839 root 1.183 return unless $SDL_MIXER;
1840    
1841 root 1.168 # music might have changed...
1842     $path eq $MUSIC_PLAYING->{path}
1843     or return &audio_music_start ();
1844    
1845     $MUSIC_PLAYER = new_from_file CFPlus::MixMusic $path;
1846    
1847     my $NOW = time;
1848    
1849     if ($MUSIC_PLAYING->{stop_time} > $NOW - $MUSIC_RESUME) {
1850     my $pos = $MUSIC_PLAYING->{stop_pos};
1851     $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
1852     $MUSIC_START = time - $pos;
1853     } else {
1854     $MUSIC_PLAYER->play (0);
1855     $MUSIC_START = time;
1856     }
1857 root 1.163
1858 root 1.168 delete $MUSIC_PLAYING->{stop_time};
1859     delete $MUSIC_PLAYING->{stop_pos};
1860 root 1.163 }
1861     }
1862    
1863     sub audio_music_changed {
1864 root 1.1 return unless $CFG->{bgm_enable};
1865 root 1.183 return unless $SDL_MIXER;
1866 root 1.1
1867 root 1.165 # default MUSIC_WANT == MUSIC_DEFAULT
1868 root 1.163 @MUSIC_WANT = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_WANT;
1869    
1870     # if the currently playing song is acceptable, let it continue
1871 root 1.164 return if $MUSIC_PLAYING
1872     && grep $MUSIC_PLAYING->{path} eq $_->{path}, @MUSIC_WANT;
1873 root 1.163
1874 root 1.168 my $NOW = time;
1875    
1876 root 1.164 if ($MUSIC_PLAYING) {
1877 root 1.168 $MUSIC_PLAYING->{stop_time} = $NOW;
1878     $MUSIC_PLAYING->{stop_pos} = $NOW - $MUSIC_START;
1879 root 1.165 CFPlus::MixMusic::fade_out 1000;
1880 root 1.163 } else {
1881 root 1.168 # sort by stop time, oldest first
1882     @MUSIC_WANT = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_WANT;
1883 root 1.163
1884 root 1.168 # if the most recently-played piece played very recently,
1885     # resume it, else choose the oldest piece for rotation.
1886     $MUSIC_PLAYING =
1887     $MUSIC_WANT[-1]{stop_time} > $NOW - $MUSIC_RESUME
1888     ? $MUSIC_WANT[-1]
1889     : $MUSIC_WANT[0];
1890 root 1.163
1891 root 1.168 audio_music_start;
1892 root 1.163 }
1893     }
1894    
1895     sub audio_music_finished {
1896     $MUSIC_PLAYING = undef;
1897     undef $MUSIC_PLAYER;
1898 root 1.1
1899 root 1.163 audio_music_changed;
1900 root 1.1 }
1901    
1902     sub audio_init {
1903     if ($CFG->{audio_enable}) {
1904 root 1.104 if (open my $fh, "<", CFPlus::find_rcfile "sounds/config") {
1905     $SDL_MIXER = !CFPlus::Mix_OpenAudio;
1906 root 1.1
1907     unless ($SDL_MIXER) {
1908     status "Unable to open sound device: there will be no sound";
1909     return;
1910     }
1911    
1912 root 1.104 CFPlus::Mix_AllocateChannels 8;
1913     CFPlus::MixMusic::volume $CFG->{bgm_volume} * 128;
1914 root 1.1
1915     audio_music_finished;
1916    
1917 root 1.129 local $_;
1918 root 1.1 while (<$fh>) {
1919     next if /^\s*#/;
1920     next if /^\s*$/;
1921    
1922     my ($file, $volume, $event) = split /\s+/, $_, 3;
1923    
1924     push @SOUNDS, "$volume,$file";
1925    
1926 root 1.129 $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1927     my $chunk = new_from_file CFPlus::MixChunk CFPlus::find_rcfile "sounds/$file";
1928     $chunk->volume ($volume * 128 / 100);
1929     $chunk
1930     };
1931 root 1.1 }
1932     } else {
1933     status "unable to open sound config: $!";
1934     }
1935 root 1.183 } else {
1936     undef $SDL_MIXER;
1937 root 1.1 }
1938     }
1939    
1940     sub audio_shutdown {
1941 root 1.104 CFPlus::Mix_CloseAudio if $SDL_MIXER;
1942 root 1.1 undef $SDL_MIXER;
1943     @SOUNDS = ();
1944     %AUDIO_CHUNKS = ();
1945     }
1946    
1947     my %animate_object;
1948     my $animate_timer;
1949    
1950     my $fps = 9;
1951    
1952     my %demo;#d#
1953    
1954     sub force_refresh {
1955     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1956 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1957 root 1.1
1958 root 1.104 $CFPlus::UI::ROOT->draw;
1959 root 1.1
1960     $WANT_REFRESH = 0;
1961 root 1.174 $CAN_REFRESH = 0;
1962 root 1.1 $LAST_REFRESH = $NOW;
1963    
1964 root 1.104 CFPlus::SDL_GL_SwapBuffers;
1965 root 1.1 }
1966    
1967 root 1.49 my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1968 root 1.1 $NOW = time;
1969    
1970     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1971 root 1.169 for CFPlus::poll_events;
1972 root 1.1
1973     if (%animate_object) {
1974     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1975 root 1.174 ++$WANT_REFRESH;
1976 root 1.1 }
1977    
1978     if ($WANT_REFRESH) {
1979     force_refresh;
1980     } else {
1981     $CAN_REFRESH = 1;
1982     }
1983     });
1984    
1985     sub animation_start {
1986     my ($widget) = @_;
1987     $animate_object{$widget} = $widget;
1988     }
1989    
1990     sub animation_stop {
1991     my ($widget) = @_;
1992     delete $animate_object{$widget};
1993     }
1994    
1995     # check once/second for faces that need to be prefetched
1996     # this should, of course, only run on demand, but
1997     # SDL forces worse things on us....
1998    
1999     Event->timer (after => 1, interval => 0.25, cb => sub {
2000     $CONN->face_prefetch
2001     if $CONN;
2002     });
2003    
2004     %SDL_CB = (
2005 root 1.104 CFPlus::SDL_QUIT => sub {
2006 root 1.151 exit;
2007 root 1.1 },
2008 root 1.104 CFPlus::SDL_VIDEORESIZE => sub {
2009 root 1.1 },
2010 root 1.104 CFPlus::SDL_VIDEOEXPOSE => sub {
2011     CFPlus::UI::full_refresh;
2012 root 1.1 },
2013 root 1.104 CFPlus::SDL_ACTIVEEVENT => sub {
2014 root 1.137 # not useful, as APPACTIVE include sonly iconified state, not unmapped
2015 root 1.171 # printf "active %x %x %x\n", $_[0]{gain}, $_[0]{state}, CFPlus::SDL_GetAppState;#d#
2016     # printf "a %x\n", CFPlus::SDL_GetAppState & CFPlus::SDL_APPACTIVE;#d#
2017 root 1.137 # printf "A\n" if $_[0]{state} & CFPlus::SDL_APPACTIVE;
2018     # printf "K\n" if $_[0]{state} & CFPlus::SDL_APPINPUTFOCUS;
2019     # printf "M\n" if $_[0]{state} & CFPlus::SDL_APPMOUSEFOCUS;
2020 root 1.1 },
2021 root 1.104 CFPlus::SDL_KEYDOWN => sub {
2022     if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) {
2023 root 1.1 # alt-enter
2024 root 1.94 $FULLSCREEN_ENABLE->toggle;
2025 root 1.1 video_shutdown;
2026     video_init;
2027     } else {
2028 root 1.104 CFPlus::UI::feed_sdl_key_down_event ($_[0]);
2029 root 1.1 }
2030     },
2031 root 1.104 CFPlus::SDL_KEYUP => \&CFPlus::UI::feed_sdl_key_up_event,
2032     CFPlus::SDL_MOUSEMOTION => \&CFPlus::UI::feed_sdl_motion_event,
2033     CFPlus::SDL_MOUSEBUTTONDOWN => \&CFPlus::UI::feed_sdl_button_down_event,
2034     CFPlus::SDL_MOUSEBUTTONUP => \&CFPlus::UI::feed_sdl_button_up_event,
2035     CFPlus::SDL_USEREVENT => sub {
2036 root 1.1 if ($_[0]{code} == 1) {
2037     audio_channel_finished $_[0]{data1};
2038     } elsif ($_[0]{code} == 0) {
2039     audio_music_finished;
2040     }
2041     },
2042     );
2043    
2044     #############################################################################
2045    
2046     $SIG{INT} = $SIG{TERM} = sub { exit };
2047    
2048     {
2049 root 1.104 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc";
2050 root 1.147 CFPlus::DB::Server::run;
2051    
2052 root 1.104 CFPlus::UI::set_layout ($::CFG->{layout});
2053 root 1.1
2054     my %DEF_CFG = (
2055 root 1.149 sdl_mode => 0,
2056     width => 640,
2057     height => 480,
2058     fullscreen => 0,
2059     fast => 0,
2060     map_scale => 1,
2061     fow_enable => 1,
2062 root 1.173 fow_intensity => 0,
2063 root 1.149 map_smoothing => 1,
2064     gui_fontsize => 1,
2065     log_fontsize => 0.7,
2066     gauge_fontsize => 1,
2067     gauge_size => 0.35,
2068     stat_fontsize => 0.7,
2069     mapsize => 100,
2070     audio_enable => 1,
2071     bgm_enable => 1,
2072     bgm_volume => 0.25,
2073     face_prefetch => 0,
2074     output_sync => 1,
2075     output_count => 1,
2076     output_rate => "",
2077     pickup => 0,
2078     inv_sort => "mtime",
2079     default => "profile", # default profile
2080     show_tips => 1,
2081     logview_max_par => 1000,
2082 root 1.1 );
2083 root 1.75
2084 root 1.1 while (my ($k, $v) = each %DEF_CFG) {
2085     $CFG->{$k} = $v unless exists $CFG->{$k};
2086     }
2087    
2088 root 1.75 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
2089 root 1.137 $PROFILE = $CFG->{profile}{default};
2090    
2091     # convert old bindings (only default profile matters)
2092     if (my $bindings = delete $PROFILE->{bindings}) {
2093     while (my ($mod, $syms) = each %$bindings) {
2094     while (my ($sym, $cmds) = each %$syms) {
2095     push @{ $PROFILE->{macro} }, {
2096     accelkey => [$mod*1, $sym*1],
2097     action => $cmds,
2098     };
2099     }
2100     }
2101     }
2102 root 1.75
2103 root 1.1 sdl_init;
2104    
2105 root 1.172 @SDL_MODES = CFPlus::SDL_ListModes 8, 8;
2106     @SDL_MODES = CFPlus::SDL_ListModes 5, 0 unless @SDL_MODES;
2107 root 1.104 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
2108 root 1.1
2109 root 1.177 @SDL_MODES = sort { $a->[0] * $a->[1] <=> $b->[0] * $b->[1] } @SDL_MODES;
2110    
2111 root 1.1 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
2112    
2113     {
2114 root 1.104 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw(
2115 root 1.1 DejaVuSans.ttf
2116     DejaVuSansMono.ttf
2117     DejaVuSans-Bold.ttf
2118     DejaVuSansMono-Bold.ttf
2119     DejaVuSans-Oblique.ttf
2120     DejaVuSansMono-Oblique.ttf
2121     DejaVuSans-BoldOblique.ttf
2122     DejaVuSansMono-BoldOblique.ttf
2123     );
2124    
2125 root 1.104 CFPlus::add_font $_ for @fonts;
2126 root 1.1
2127 root 1.104 CFPlus::pango_init;
2128 root 1.1
2129 root 1.104 $FONT_PROP = new_from_file CFPlus::Font $fonts[0];
2130     $FONT_FIXED = new_from_file CFPlus::Font $fonts[1];
2131 root 1.1
2132     $FONT_PROP->make_default;
2133     }
2134    
2135     # compare mono (ft) vs. rgba (cairo)
2136     # ft - 1.8s, cairo 3s, even in alpha-only mode
2137     # for my $rgba (0..1) {
2138     # my $t1 = Time::HiRes::time;
2139     # for (1..1000) {
2140 root 1.104 # my $layout = CFPlus::Layout->new ($rgba);
2141 root 1.1 # $layout->set_text ("hallo" x 100);
2142     # $layout->render;
2143     # }
2144     # my $t2 = Time::HiRes::time;
2145     # warn $t2-$t1;
2146     # }
2147    
2148 root 1.122 $startup_done->();
2149    
2150 root 1.1 video_init;
2151     audio_init;
2152     }
2153    
2154 root 1.119 show_tip_of_the_day if $CFG->{show_tips};
2155    
2156 root 1.1 Event::loop;
2157 root 1.104 #CFPlus::SDL_Quit;
2158     #CFPlus::_exit 0;
2159 root 1.1
2160 root 1.150 END {
2161 root 1.151 CFPlus::SDL_Quit;
2162 root 1.150 CFPlus::DB::Server::stop;
2163     }
2164 root 1.1
2165     =head1 NAME
2166    
2167 root 1.28 cfplus - A Crossfire+ and Crossfire game client
2168 root 1.1
2169     =head1 SYNOPSIS
2170    
2171     Just run it - no commandline arguments are supported.
2172    
2173     =head1 USAGE
2174    
2175 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
2176 root 1.1 fullscreen and interactively.
2177    
2178 root 1.39 =head1 DEBUGGING
2179    
2180    
2181     CFPLUS_DEBUG - environment variable
2182    
2183     1 draw borders around widgets
2184     2 add low-level widget info to tooltips
2185     4 show fps
2186     8 suppress tooltips
2187    
2188 root 1.1 =head1 AUTHOR
2189    
2190     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
2191    
2192    
2193