ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/deliantra
Revision: 1.29
Committed: Mon Mar 24 00:24:47 2008 UTC (16 years, 3 months ago) by root
Branch: MAIN
Changes since 1.28: +51 -4 lines
Log Message:
*** empty log message ***

File Contents

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