ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.196
Committed: Tue Jul 31 02:03:13 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.195: +25 -19 lines
Log Message:
more music protocol changes

File Contents

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