ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.224
Committed: Thu Aug 30 07:57:59 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.223: +10 -3 lines
Log Message:
oh my, cfplus always played the same song when given the choice of many

File Contents

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