ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/deliantra
Revision: 1.12
Committed: Wed Dec 26 13:35:06 2007 UTC (16 years, 5 months ago) by root
Branch: MAIN
Changes since 1.11: +3 -3 lines
Log Message:
*** empty log message ***

File Contents

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