ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.216
Committed: Sun Aug 19 12:56:24 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.215: +39 -29 lines
Log Message:
use and enable texture compression by default

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