ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.194
Committed: Tue Jul 31 00:50:04 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.193: +8 -7 lines
Log Message:
better channel management, bugfixes, volume 0..1

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 root 1.194 our %AUDIO_CHUNK; # audio files
174 root 1.1
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.194 &audio_music_set_ambient ([]);
487 root 1.166
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 root 1.194 my $channel = CFPlus::Channel::find;
1809 root 1.191 $channel->volume (128 + $vol);
1810     $dx = $dx / 10 * 255;
1811     $channel->set_panning ((min 255, 255 - $dx), (min 255, 255 + $dx));
1812 root 1.194
1813 root 1.191 # my $angle = $dx ? : $dx < 0 ?
1814     # my $distance = -$vol;
1815     # $channel->set_position ($angle, $distance);
1816 root 1.194
1817     $chunk->play ($channel);
1818 root 1.191 }
1819     } else {
1820     # sound_meta not set means data is in flight either way
1821     my $meta = $CONN->{sound_meta}{$face}
1822     or return;
1823    
1824     # fetch from database
1825     CFPlus::DB::get res_data => $meta->{name}, sub {
1826 root 1.193 my $vol = $meta->{meta}{volume} || 1;
1827 root 1.191 my $rwops = new CFPlus::RW $_[0];
1828     my $chunk = new CFPlus::MixChunk $rwops;
1829 root 1.193 $chunk->volume ($vol * 128);
1830 root 1.191 $AUDIO_CHUNK{$face} = $chunk;
1831    
1832     audio_sound_push ($face);
1833     };
1834     }
1835     }
1836    
1837     sub audio_sound_play {
1838     my ($face, $dx, $dy, $vol) = @_;
1839    
1840     $SDL_MIXER
1841     or return;
1842    
1843     my $queue = $AUDIO_PLAY{$face} ||= [];
1844     push @$queue, [Event::time + 0.2, $dx, $dy, $vol]; # delay sound by max. 0.2s
1845     audio_sound_push $face
1846     unless @$queue > 1;
1847 root 1.1 }
1848    
1849 root 1.194 sub audio_music_set_ambient {
1850 root 1.163 my ($songs) = @_;
1851    
1852 root 1.168 my @want =
1853     grep $_,
1854     map $CONN->{music_meta}{$_},
1855     @$songs;
1856 root 1.163
1857 root 1.168 if (@want) {
1858     @MUSIC_WANT = @want;
1859     &audio_music_changed ();
1860     }
1861     }
1862    
1863     sub audio_music_start {
1864 root 1.190 my $meta = $MUSIC_PLAYING_META;
1865 root 1.168
1866 root 1.190 CFPlus::DB::get res_data => $meta->{name}, sub {
1867 root 1.183 return unless $SDL_MIXER;
1868    
1869 root 1.168 # music might have changed...
1870 root 1.190 $meta eq $MUSIC_PLAYING_META
1871 root 1.168 or return &audio_music_start ();
1872    
1873 root 1.190 $MUSIC_PLAYING_DATA = \$_[0];
1874    
1875     my $rwops = $meta->{path}
1876     ? new_from_file CFPlus::RW $meta->{path}
1877     : new CFPlus::RW $$MUSIC_PLAYING_DATA;
1878    
1879     $MUSIC_PLAYER = new CFPlus::MixMusic $rwops
1880     or ((warn CFPlus::Mix_GetError), return); # pretty fatal error
1881 root 1.168
1882     my $NOW = time;
1883    
1884 root 1.190 if ($MUSIC_PLAYING_META->{stop_time} > $NOW - $MUSIC_RESUME) {
1885     my $pos = $MUSIC_PLAYING_META->{stop_pos};
1886 root 1.168 $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
1887     $MUSIC_START = time - $pos;
1888     } else {
1889     $MUSIC_PLAYER->play (0);
1890     $MUSIC_START = time;
1891     }
1892 root 1.163
1893 root 1.190 delete $MUSIC_PLAYING_META->{stop_time};
1894     delete $MUSIC_PLAYING_META->{stop_pos};
1895 root 1.163 }
1896     }
1897    
1898     sub audio_music_changed {
1899 root 1.1 return unless $CFG->{bgm_enable};
1900 root 1.183 return unless $SDL_MIXER;
1901 root 1.1
1902 root 1.165 # default MUSIC_WANT == MUSIC_DEFAULT
1903 root 1.163 @MUSIC_WANT = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_WANT;
1904    
1905     # if the currently playing song is acceptable, let it continue
1906 root 1.190 return if $MUSIC_PLAYING_META
1907     && grep $MUSIC_PLAYING_META == $_, @MUSIC_WANT;
1908 root 1.163
1909 root 1.168 my $NOW = time;
1910    
1911 root 1.190 if ($MUSIC_PLAYING_META) {
1912     $MUSIC_PLAYING_META->{stop_time} = $NOW;
1913     $MUSIC_PLAYING_META->{stop_pos} = $NOW - $MUSIC_START;
1914 root 1.165 CFPlus::MixMusic::fade_out 1000;
1915 root 1.163 } else {
1916 root 1.168 # sort by stop time, oldest first
1917     @MUSIC_WANT = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_WANT;
1918 root 1.163
1919 root 1.168 # if the most recently-played piece played very recently,
1920     # resume it, else choose the oldest piece for rotation.
1921 root 1.190 $MUSIC_PLAYING_META =
1922 root 1.168 $MUSIC_WANT[-1]{stop_time} > $NOW - $MUSIC_RESUME
1923     ? $MUSIC_WANT[-1]
1924     : $MUSIC_WANT[0];
1925 root 1.163
1926 root 1.168 audio_music_start;
1927 root 1.163 }
1928     }
1929    
1930     sub audio_music_finished {
1931     undef $MUSIC_PLAYER;
1932 root 1.190 undef $MUSIC_PLAYING_META;
1933     undef $MUSIC_PLAYING_DATA;
1934 root 1.1
1935 root 1.163 audio_music_changed;
1936 root 1.1 }
1937    
1938     sub audio_init {
1939     if ($CFG->{audio_enable}) {
1940 root 1.191 $ENV{MIX_EFFECTSMAXSPEED} = 1;
1941     $SDL_MIXER = !CFPlus::Mix_OpenAudio;
1942 root 1.1
1943 root 1.191 unless ($SDL_MIXER) {
1944     status "Unable to open sound device: there will be no sound";
1945     return;
1946     }
1947 root 1.1
1948 root 1.191 CFPlus::Mix_AllocateChannels 16;
1949     CFPlus::MixMusic::volume $CFG->{bgm_volume} * 128;
1950 root 1.1
1951 root 1.191 audio_music_finished;
1952 root 1.183 } else {
1953     undef $SDL_MIXER;
1954 root 1.1 }
1955     }
1956    
1957     sub audio_shutdown {
1958 root 1.104 CFPlus::Mix_CloseAudio if $SDL_MIXER;
1959 root 1.1 undef $SDL_MIXER;
1960     @SOUNDS = ();
1961 root 1.194 %AUDIO_CHUNK = ();
1962 root 1.1 }
1963    
1964     my %animate_object;
1965     my $animate_timer;
1966    
1967     my $fps = 9;
1968    
1969     sub force_refresh {
1970     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1971 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1972 root 1.1
1973 root 1.104 $CFPlus::UI::ROOT->draw;
1974 root 1.1
1975     $WANT_REFRESH = 0;
1976 root 1.174 $CAN_REFRESH = 0;
1977 root 1.1 $LAST_REFRESH = $NOW;
1978    
1979 root 1.104 CFPlus::SDL_GL_SwapBuffers;
1980 root 1.1 }
1981    
1982 root 1.49 my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1983 root 1.1 $NOW = time;
1984    
1985     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1986 root 1.169 for CFPlus::poll_events;
1987 root 1.1
1988     if (%animate_object) {
1989     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1990 root 1.174 ++$WANT_REFRESH;
1991 root 1.1 }
1992    
1993     if ($WANT_REFRESH) {
1994     force_refresh;
1995     } else {
1996     $CAN_REFRESH = 1;
1997     }
1998     });
1999    
2000     sub animation_start {
2001     my ($widget) = @_;
2002     $animate_object{$widget} = $widget;
2003     }
2004    
2005     sub animation_stop {
2006     my ($widget) = @_;
2007     delete $animate_object{$widget};
2008     }
2009    
2010     %SDL_CB = (
2011 root 1.104 CFPlus::SDL_QUIT => sub {
2012 root 1.151 exit;
2013 root 1.1 },
2014 root 1.104 CFPlus::SDL_VIDEORESIZE => sub {
2015 root 1.1 },
2016 root 1.104 CFPlus::SDL_VIDEOEXPOSE => sub {
2017     CFPlus::UI::full_refresh;
2018 root 1.1 },
2019 root 1.104 CFPlus::SDL_ACTIVEEVENT => sub {
2020 root 1.137 # not useful, as APPACTIVE include sonly iconified state, not unmapped
2021 root 1.171 # printf "active %x %x %x\n", $_[0]{gain}, $_[0]{state}, CFPlus::SDL_GetAppState;#d#
2022     # printf "a %x\n", CFPlus::SDL_GetAppState & CFPlus::SDL_APPACTIVE;#d#
2023 root 1.137 # printf "A\n" if $_[0]{state} & CFPlus::SDL_APPACTIVE;
2024     # printf "K\n" if $_[0]{state} & CFPlus::SDL_APPINPUTFOCUS;
2025     # printf "M\n" if $_[0]{state} & CFPlus::SDL_APPMOUSEFOCUS;
2026 root 1.1 },
2027 root 1.104 CFPlus::SDL_KEYDOWN => sub {
2028     if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) {
2029 root 1.1 # alt-enter
2030 root 1.94 $FULLSCREEN_ENABLE->toggle;
2031 root 1.1 video_shutdown;
2032     video_init;
2033     } else {
2034 root 1.104 CFPlus::UI::feed_sdl_key_down_event ($_[0]);
2035 root 1.1 }
2036     },
2037 root 1.104 CFPlus::SDL_KEYUP => \&CFPlus::UI::feed_sdl_key_up_event,
2038     CFPlus::SDL_MOUSEMOTION => \&CFPlus::UI::feed_sdl_motion_event,
2039     CFPlus::SDL_MOUSEBUTTONDOWN => \&CFPlus::UI::feed_sdl_button_down_event,
2040     CFPlus::SDL_MOUSEBUTTONUP => \&CFPlus::UI::feed_sdl_button_up_event,
2041     CFPlus::SDL_USEREVENT => sub {
2042 root 1.1 if ($_[0]{code} == 1) {
2043     audio_channel_finished $_[0]{data1};
2044     } elsif ($_[0]{code} == 0) {
2045     audio_music_finished;
2046     }
2047     },
2048     );
2049    
2050     #############################################################################
2051    
2052     $SIG{INT} = $SIG{TERM} = sub { exit };
2053    
2054     {
2055 root 1.104 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc";
2056 root 1.147 CFPlus::DB::Server::run;
2057    
2058 root 1.104 CFPlus::UI::set_layout ($::CFG->{layout});
2059 root 1.1
2060     my %DEF_CFG = (
2061 root 1.149 sdl_mode => 0,
2062     fullscreen => 0,
2063     fast => 0,
2064     map_scale => 1,
2065     fow_enable => 1,
2066 root 1.173 fow_intensity => 0,
2067 root 1.149 map_smoothing => 1,
2068     gui_fontsize => 1,
2069     log_fontsize => 0.7,
2070     gauge_fontsize => 1,
2071     gauge_size => 0.35,
2072     stat_fontsize => 0.7,
2073     mapsize => 100,
2074     audio_enable => 1,
2075     bgm_enable => 1,
2076     bgm_volume => 0.25,
2077     output_sync => 1,
2078     output_count => 1,
2079     output_rate => "",
2080     pickup => 0,
2081     inv_sort => "mtime",
2082     default => "profile", # default profile
2083     show_tips => 1,
2084     logview_max_par => 1000,
2085 root 1.1 );
2086 root 1.75
2087 root 1.1 while (my ($k, $v) = each %DEF_CFG) {
2088     $CFG->{$k} = $v unless exists $CFG->{$k};
2089     }
2090    
2091 root 1.75 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
2092 root 1.137 $PROFILE = $CFG->{profile}{default};
2093    
2094     # convert old bindings (only default profile matters)
2095     if (my $bindings = delete $PROFILE->{bindings}) {
2096     while (my ($mod, $syms) = each %$bindings) {
2097     while (my ($sym, $cmds) = each %$syms) {
2098     push @{ $PROFILE->{macro} }, {
2099     accelkey => [$mod*1, $sym*1],
2100     action => $cmds,
2101     };
2102     }
2103     }
2104     }
2105 root 1.75
2106 root 1.1 sdl_init;
2107    
2108 root 1.172 @SDL_MODES = CFPlus::SDL_ListModes 8, 8;
2109     @SDL_MODES = CFPlus::SDL_ListModes 5, 0 unless @SDL_MODES;
2110 root 1.104 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
2111 root 1.1
2112 root 1.177 @SDL_MODES = sort { $a->[0] * $a->[1] <=> $b->[0] * $b->[1] } @SDL_MODES;
2113    
2114 root 1.1 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
2115    
2116     {
2117 root 1.104 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw(
2118 root 1.1 DejaVuSans.ttf
2119     DejaVuSansMono.ttf
2120     DejaVuSans-Bold.ttf
2121     DejaVuSansMono-Bold.ttf
2122     DejaVuSans-Oblique.ttf
2123     DejaVuSansMono-Oblique.ttf
2124     DejaVuSans-BoldOblique.ttf
2125     DejaVuSansMono-BoldOblique.ttf
2126     );
2127    
2128 root 1.104 CFPlus::add_font $_ for @fonts;
2129 root 1.1
2130 root 1.104 CFPlus::pango_init;
2131 root 1.1
2132 root 1.104 $FONT_PROP = new_from_file CFPlus::Font $fonts[0];
2133     $FONT_FIXED = new_from_file CFPlus::Font $fonts[1];
2134 root 1.1
2135     $FONT_PROP->make_default;
2136     }
2137    
2138     # compare mono (ft) vs. rgba (cairo)
2139     # ft - 1.8s, cairo 3s, even in alpha-only mode
2140     # for my $rgba (0..1) {
2141     # my $t1 = Time::HiRes::time;
2142     # for (1..1000) {
2143 root 1.104 # my $layout = CFPlus::Layout->new ($rgba);
2144 root 1.1 # $layout->set_text ("hallo" x 100);
2145     # $layout->render;
2146     # }
2147     # my $t2 = Time::HiRes::time;
2148     # warn $t2-$t1;
2149     # }
2150    
2151 root 1.122 $startup_done->();
2152    
2153 root 1.1 video_init;
2154     audio_init;
2155     }
2156    
2157 root 1.119 show_tip_of_the_day if $CFG->{show_tips};
2158    
2159 root 1.1 Event::loop;
2160 root 1.104 #CFPlus::SDL_Quit;
2161     #CFPlus::_exit 0;
2162 root 1.1
2163 root 1.150 END {
2164 root 1.151 CFPlus::SDL_Quit;
2165 root 1.150 CFPlus::DB::Server::stop;
2166     }
2167 root 1.1
2168     =head1 NAME
2169    
2170 root 1.28 cfplus - A Crossfire+ and Crossfire game client
2171 root 1.1
2172     =head1 SYNOPSIS
2173    
2174     Just run it - no commandline arguments are supported.
2175    
2176     =head1 USAGE
2177    
2178 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
2179 root 1.1 fullscreen and interactively.
2180    
2181 root 1.39 =head1 DEBUGGING
2182    
2183    
2184     CFPLUS_DEBUG - environment variable
2185    
2186     1 draw borders around widgets
2187     2 add low-level widget info to tooltips
2188     4 show fps
2189     8 suppress tooltips
2190    
2191 root 1.1 =head1 AUTHOR
2192    
2193     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
2194    
2195    
2196