ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/deliantra
Revision: 1.25
Committed: Sun Jan 6 17:40:45 2008 UTC (16 years, 4 months ago) by elmex
Branch: MAIN
CVS Tags: rel-0_9963
Changes since 1.24: +1 -1 lines
Log Message:
fixed a small bug in the message window naming

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