ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.193
Committed: Tue Jul 31 00:30:16 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.192: +2 -6 lines
Log Message:
normalise sound

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