ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.219
Committed: Tue Aug 21 00:59:34 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.218: +16 -12 lines
Log Message:
- reworked the fxix handling more or less completely. i do see a structure now,
  but haven't implemented it fully. what we have will have to do.
- added generic flat resource support
- used this to retrieve the exp table resource
- cleaned up face handling (uses less memory now)
- implemented clean callbacks for face changes
- halved latency for tile loading

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