ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.227
Committed: Sun Sep 2 09:48:42 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-0_99, rel-2_2
Changes since 1.226: +17 -16 lines
Log Message:
some binary release infrastructure changes, fix keybindings by prefering raw ones over unicode ones

File Contents

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