ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.199
Committed: Wed Aug 1 14:27:21 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.198: +31 -11 lines
Log Message:
UI tweaks, effects volume

File Contents

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