ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.198
Committed: Tue Jul 31 02:45:39 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.197: +62 -53 lines
Log Message:
grind out se bugs

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