ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.190
Committed: Sun Jul 29 18:56:03 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.189: +25 -18 lines
Log Message:
put music and other resources into the database, now that we can play form memory

File Contents

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