ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.197
Committed: Tue Jul 31 02:25:52 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.196: +50 -35 lines
Log Message:
crudely implement audio jingles

File Contents

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