ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.225
Committed: Fri Aug 31 08:03:10 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.224: +1 -2 lines
Log Message:
da muss man erstmal draufkommen

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