ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.226
Committed: Sat Sep 1 07:22:21 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.225: +2 -0 lines
Log Message:
simpler, more powerful and slightly less compact widget protocol

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