ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.195
Committed: Tue Jul 31 01:03:52 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.194: +161 -148 lines
Log Message:
respect music base volume

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