ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.218
Committed: Sun Aug 19 14:17:05 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.217: +2 -1 lines
Log Message:
*** empty log message ***

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 root 1.218 . "can differ form card to card, so your mileage may vary. This setting is ignored in "
790     . "forced OpenGL 1.1 mode.",
791 root 1.216 on_changed => sub { my ($self, $value) = @_; $CFG->{texture_compression} = $value; 0 }
792 root 1.210 );
793    
794 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
795     $table->add_at (1, $row++, new CFPlus::UI::CheckBox
796 root 1.1 state => $CFG->{fast},
797     tooltip => "Lower the visual quality considerably to speed up rendering.",
798 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fast} = $value; 0 }
799 root 1.1 );
800    
801 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
802     $table->add_at (1, $row++, new CFPlus::UI::Slider
803 root 1.149 range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
804     tooltip => "The base font size used by most GUI elements that do not have their own setting.",
805     on_changed => sub { $CFG->{gui_fontsize} = $_[1]; 0 },
806     );
807    
808 root 1.178 $table->add_at (1, $row++, new CFPlus::UI::Button
809 root 1.149 expand => 1, align => 0, text => "Apply",
810     tooltip => "Apply the video settings above.",
811     on_activate => sub {
812     video_shutdown ();
813     video_init ();
814     0
815     }
816     );
817    
818 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Scale");
819     $table->add_at (1, $row++, new CFPlus::UI::Slider
820 root 1.1 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
821     tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
822 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 }
823 root 1.1 );
824    
825 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing");
826     $table->add_at (1, $row++, new CFPlus::UI::CheckBox
827 root 1.149 state => $CFG->{map_smoothing},
828     tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. "
829 root 1.209 . "This increases load on the graphics subsystem and works only with TRT servers. "
830 root 1.210 . "Changes take effect at next login only.",
831 root 1.149 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 }
832     );
833    
834 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War");
835     $table->add_at (1, $row++, new CFPlus::UI::CheckBox
836 root 1.1 state => $CFG->{fow_enable},
837     tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.",
838 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 }
839 root 1.1 );
840    
841 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "FoW Intensity");
842     $table->add_at (1, $row++, new CFPlus::UI::Slider
843 root 1.1 range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256],
844     tooltip => "<b>Fog of War Lightness.</b> The higher the intensity, the lighter the Fog-of-War color. Changes are instant.",
845 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_intensity} = $value; 0 }
846 root 1.1 );
847    
848 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Message Fontsize");
849     $table->add_at (1, $row++, new CFPlus::UI::Slider
850 root 1.1 range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1],
851 root 1.210 tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant, "
852     . "but you still need to press apply to correctly re-layout the widget.",
853 elmex 1.182 on_changed => sub { $MESSAGE_WINDOW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 },
854 root 1.1 );
855    
856 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
857     $table->add_at (1, $row++, new CFPlus::UI::Slider
858 root 1.1 range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1],
859     tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.",
860 root 1.18 on_changed => sub {
861 root 1.1 $CFG->{gauge_fontsize} = $_[1];
862     &set_gauge_window_fontsize;
863 root 1.74 0
864 root 1.1 }
865     );
866    
867 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge size");
868     $table->add_at (1, $row++, new CFPlus::UI::Slider
869 root 1.18 range => [$CFG->{gauge_size}, 0.2, 0.8],
870     tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.",
871     on_changed => sub {
872 root 1.1 $CFG->{gauge_size} = $_[1];
873     $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
874 root 1.74 0
875 root 1.1 }
876     );
877    
878 root 1.49 $vbox
879     }
880    
881     sub audio_setup {
882 root 1.104 my $vbox = new CFPlus::UI::VBox;
883 root 1.49
884 root 1.199 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 0, 1]);
885 root 1.49
886     my $row = 0;
887    
888 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable");
889     $table->add_at (1, $row++, new CFPlus::UI::CheckBox
890 root 1.1 state => $CFG->{audio_enable},
891     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.",
892 root 1.74 on_changed => sub { $CFG->{audio_enable} = $_[1]; 0 }
893 root 1.1 );
894 root 1.178 # $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume");
895     # $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
896 root 1.1 # $CFG->{effects_volume} = $_[1];
897     # });
898 root 1.199
899 root 1.205 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Sound Effects");
900 root 1.199 $table->add_at (1, $row, new CFPlus::UI::CheckBox
901     expand => 1, state => $CFG->{effects_enable},
902     tooltip => "If enabled, sound effects are enabled. If disabled, no sound effects will be played.",
903 root 1.205 on_changed => sub {
904     $CFG->{effects_enable} = $_[1];
905     $CONN->update_fx_want if $CONN;
906     0
907     }
908 root 1.199 );
909     $table->add_at (2, $row++, new CFPlus::UI::Slider
910     expand => 1, range => [$CFG->{effects_volume}, 0, 1, 0, 1/128],
911     tooltip => "The relative volume of sound effects. Best audio quality is achieved if this "
912 root 1.205 . "is set highest (rightmost) and you use your operating system volume setting. Changes are instant.",
913 root 1.199 on_changed => sub { $CFG->{effects_volume} = $_[1]; 0 }
914     );
915    
916 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music");
917 root 1.199 $table->add_at (1, $row, new CFPlus::UI::CheckBox
918 root 1.1 expand => 1, state => $CFG->{bgm_enable},
919     tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
920 root 1.205 on_changed => sub {
921     $CFG->{bgm_enable} = $_[1];
922     $CONN->update_fx_want if $CONN;
923     0
924     }
925 root 1.1 );
926 root 1.199 $table->add_at (2, $row++, new CFPlus::UI::Slider
927 root 1.1 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
928     tooltip => "The volume of the background music. Changes are instant.",
929 root 1.195 on_changed => sub { $CFG->{bgm_volume} = $_[1]; audio_music_update_volume; 0 }
930 root 1.1 );
931    
932 root 1.178 $table->add_at (1, $row++, new CFPlus::UI::Button
933 root 1.199 c_colspan => 2, expand => 1, align => 0, text => "Apply",
934 root 1.1 tooltip => "Apply the audio settings",
935 root 1.18 on_activate => sub {
936 root 1.1 audio_shutdown ();
937     audio_init ();
938 root 1.74 0
939 root 1.1 }
940     );
941    
942 root 1.49 $vbox
943 root 1.1 }
944    
945     sub set_gauge_window_fontsize {
946     for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
947     $_->set_fontsize ($::CFG->{gauge_fontsize});
948     }
949     }
950    
951     sub make_gauge_window {
952     my $gh = int $HEIGHT * $CFG->{gauge_size};
953    
954 root 1.104 my $win = new CFPlus::UI::Frame (
955 root 1.30 force_x => 0,
956     force_y => "max",
957     force_w => $WIDTH,
958     force_h => $gh,
959 root 1.1 );
960    
961 root 1.104 $win->add (my $hbox = new CFPlus::UI::HBox
962 root 1.1 children => [
963 root 1.104 (new CFPlus::UI::HBox expand => 1),
964     (new CFPlus::UI::VBox children => [
965     (new CFPlus::UI::Empty expand => 1),
966     (new CFPlus::UI::Frame bg => [0, 0, 0, 0.4], child => ($FLOORBOX = new CFPlus::UI::Table)),
967 root 1.1 ]),
968 root 1.104 (my $vbox = new CFPlus::UI::VBox),
969 root 1.1 ],
970     );
971    
972 root 1.104 $vbox->add (new CFPlus::UI::HBox
973 root 1.1 expand => 1,
974     children => [
975 root 1.104 (new CFPlus::UI::Empty expand => 1),
976     (my $hb = new CFPlus::UI::HBox),
977 root 1.1 ],
978     );
979    
980 root 1.109 $hb->add (my $hg = new CFPlus::UI::Gauge type => 'hp', tooltip => "#stat_health");
981     $hb->add (my $mg = new CFPlus::UI::Gauge type => 'mana', tooltip => "#stat_mana");
982     $hb->add (my $gg = new CFPlus::UI::Gauge type => 'grace', tooltip => "#stat_grace");
983     $hb->add (my $fg = new CFPlus::UI::Gauge type => 'food', tooltip => "#stat_food");
984    
985     $vbox->add (my $exp = new CFPlus::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1, tooltip => "#stat_exp");
986     $vbox->add (my $rng = new CFPlus::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1, tooltip => "#stat_ranged");
987 root 1.1
988     $GAUGES = {
989     exp => $exp, win => $win, range => $rng,
990     food => $fg, mana => $mg, hp => $hg, grace => $gg
991     };
992    
993     &set_gauge_window_fontsize;
994    
995     $win
996     }
997    
998 root 1.65 sub debug_setup {
999 root 1.104 my $table = new CFPlus::UI::Table;
1000 root 1.65
1001 root 1.178 $table->add_at (0, 0, new CFPlus::UI::Label text => "Widget Borders");
1002     $table->add_at (1, 0, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 1; 0 });
1003     $table->add_at (0, 1, new CFPlus::UI::Label text => "Tooltip Widget Info");
1004     $table->add_at (1, 1, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 2; 0 });
1005     $table->add_at (0, 2, new CFPlus::UI::Label text => "Show FPS");
1006     $table->add_at (1, 2, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 4; 0 });
1007     $table->add_at (0, 3, new CFPlus::UI::Label text => "Suppress Tooltips");
1008     $table->add_at (1, 3, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 });
1009     $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } );
1010 root 1.65
1011 root 1.178 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d#
1012 root 1.65
1013 root 1.181 $table->add_at (7,7, my $t = new CFPlus::UI::Table expand => 0);
1014 root 1.199 $t->add_at (0,0, new CFPlus::UI::Label text => "a a a a", c_rowspan => 1, c_colspan => 2);
1015     $t->add_at (2,0, new CFPlus::UI::Label text => "b\nb", c_rowspan => 2, c_colspan => 1);
1016     $t->add_at (1,2, new CFPlus::UI::Label text => "c c c c", c_rowspan => 1, c_colspan => 2);
1017     $t->add_at (0,1, new CFPlus::UI::Label text => "d\nd", c_rowspan => 2, c_colspan => 1);
1018 root 1.179 $t->add_at (1,1, new CFPlus::UI::Label text => "e");
1019    
1020 root 1.181 $table->add_at (7, 6, my $c = new CFPlus::UI::Canvas);
1021    
1022     $c->add_items ({
1023     type => "line_loop",
1024     color => [0, 1, 0],
1025     width => 9,
1026     coord_mode => "abs",
1027     coord => [[10, 5], [5, 50], [20, 5], [5, 60]],
1028     });
1029    
1030     $c->add_items ({
1031     type => "lines",
1032     color => [1, 1, 0],
1033     width => 2,
1034     coord_mode => "rel",
1035     coord => [[0,0], [1,1], [1,0], [0,1]],
1036     });
1037    
1038     $c->add_items ({
1039     type => "polygon",
1040     color => [0, 0.43, 0],
1041     width => 2,
1042     coord_mode => "rel",
1043     coord => [[0,0.2], [1,.4], [1,.6], [0,.8]],
1044     });
1045    
1046 root 1.65 $table
1047     }
1048 elmex 1.24
1049 root 1.60 sub stats_window {
1050 root 1.104 my $r = new CFPlus::UI::ScrolledWindow (
1051 root 1.98 expand => 1,
1052     scroll_y => 1
1053     );
1054 root 1.104 $r->add (my $vb = new CFPlus::UI::VBox);
1055 root 1.1
1056 root 1.114 $vb->add (new CFPlus::UI::FancyFrame
1057     label => "Player",
1058     child => (my $pi = new CFPlus::UI::VBox),
1059     );
1060    
1061     $pi->add ($STATWIDS->{title} = new CFPlus::UI::Label valign => 0, align => -1, text => "Title:", expand => 1,
1062 root 1.1 can_hover => 1, can_events => 1,
1063     tooltip => "Your name and title. You can change your title by using the <b>title</b> command, if supported by the server.");
1064 root 1.114 $pi->add ($STATWIDS->{map} = new CFPlus::UI::Label valign => 0, align => -1, text => "Map:", expand => 1,
1065 root 1.1 can_hover => 1, can_events => 1,
1066     tooltip => "The map you are currently on (if supported by the server).");
1067    
1068 root 1.114 $pi->add (my $hb0 = new CFPlus::UI::HBox);
1069 root 1.104 $hb0->add ($STATWIDS->{weight} = new CFPlus::UI::Label valign => 0, align => -1, text => "Weight:", expand => 1,
1070 elmex 1.5 can_hover => 1, can_events => 1,
1071 root 1.15 tooltip => "The weight of the player including all inventory items.");
1072 root 1.104 $hb0->add ($STATWIDS->{m_weight} = new CFPlus::UI::Label valign => 0, align => -1, text => "Max weight:", expand => 1,
1073 elmex 1.5 can_hover => 1, can_events => 1,
1074 root 1.15 tooltip => "The weight limit: you cannot carry more than this.");
1075 elmex 1.5
1076 root 1.114 $vb->add (new CFPlus::UI::FancyFrame
1077     label => "Primary/Secondary Statistics",
1078     child => (my $hb = new CFPlus::UI::HBox expand => 1),
1079     );
1080 root 1.104 $hb->add (my $tbl = new CFPlus::UI::Table expand => 1);
1081 root 1.1
1082     my $color2 = [1, 1, 0];
1083    
1084     for (
1085 root 1.98 [0, 0, st_str => "Str", 30],
1086     [0, 1, st_dex => "Dex", 30],
1087     [0, 2, st_con => "Con", 30],
1088     [0, 3, st_int => "Int", 30],
1089     [0, 4, st_wis => "Wis", 30],
1090     [0, 5, st_pow => "Pow", 30],
1091     [0, 6, st_cha => "Cha", 30],
1092    
1093     [2, 0, st_wc => "Wc", -120],
1094     [2, 1, st_ac => "Ac", -120],
1095     [2, 2, st_dam => "Dam", 120],
1096     [2, 3, st_arm => "Arm", 120],
1097     [2, 4, st_spd => "Spd", 10.54],
1098     [2, 5, st_wspd => "WSp", 10.54],
1099 root 1.1 ) {
1100 root 1.98 my ($col, $row, $id, $label, $template) = @$_;
1101 root 1.1
1102 root 1.178 $tbl->add_at ($col , $row, $STATWIDS->{$id} = new CFPlus::UI::Label
1103 root 1.98 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0,
1104 root 1.107 align => +1, template => $template, tooltip => "#stat_$label");
1105 root 1.178 $tbl->add_at ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFPlus::UI::Label
1106 root 1.98 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0,
1107 root 1.107 align => -1, text => $label, tooltip => "#stat_$label");
1108 root 1.1 }
1109    
1110 root 1.114 $vb->add (new CFPlus::UI::FancyFrame
1111     label => "Resistancies",
1112     child => (my $tbl2 = new CFPlus::UI::Table expand => 1),
1113     );
1114 root 1.1
1115     my $row = 0;
1116     my $col = 0;
1117    
1118     my %resist_names = (
1119 elmex 1.92 slow => ["Slow",
1120     "<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.)"],
1121     holyw => ["Holy Word",
1122     "<b>Holy Word</b> (resistance you against getting the fear when someone whose god doesn't like you spells the holy word on you.)"],
1123     conf => ["Confusion",
1124     "<b>Confusion</b> (If you are hit by confusion you will move into random directions, and likely into monsters.)"],
1125     fire => ["Fire",
1126     "<b>Fire</b> (just your resistance to fire spells like burning hands, dragonbreath, meteor swarm fire, ...)"],
1127     depl => ["Depletion",
1128     "<b>Depletion</b> (some monsters and other effects can cause stats depletion)"],
1129     magic => ["Magic",
1130     "<b>Magic</b> (resistance to magic spells like magic missile or similar)"],
1131     drain => ["Draining",
1132     "<b>Draining</b> (some monsters (e.g. vampires) and other effects can steal experience)"],
1133     acid => ["Acid",
1134     "<b>Acid</b> (resistance to acid, acid hurts pretty much and also corrodes your weapons)"],
1135     pois => ["Poison",
1136     "<b>Poison</b> (resistance to getting poisoned)"],
1137     para => ["Paralysation",
1138     "<b>Paralysation</b> (this resistance affects the chance you get paralysed)"],
1139     deat => ["Death",
1140     "<b>Death</b> (resistance against death spells)"],
1141     phys => ["Physical",
1142     "<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.)"],
1143     blind => ["Blind",
1144     "<b>Blind</b> (blind resistance affects the chance of a successful blinding attack)"],
1145     fear => ["Fear",
1146     "<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)"],
1147     tund => ["Turn undead",
1148     "<b>Turn undead</b> (affects your resistancy to various forms of 'turn undead' spells. Only relevant when you are, in fact, undead..."],
1149     elec => ["Electricity",
1150     "<b>Electricity</b> (resistance against electricity, spells like large lightning, small lightning, ...)"],
1151     cold => ["Cold",
1152     "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)"],
1153     ghit => ["Ghost hit",
1154     "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)"],
1155 root 1.1 );
1156 root 1.178
1157 root 1.1 for (qw/slow holyw conf fire depl magic
1158     drain acid pois para deat phys
1159     blind fear tund elec cold ghit/)
1160     {
1161 root 1.178 $tbl2->add_at ($col, $row,
1162 root 1.1 $STATWIDS->{"res_$_"} =
1163 root 1.104 new CFPlus::UI::Label
1164 root 1.1 font => $FONT_FIXED,
1165     template => "-100%",
1166     align => +1,
1167     valign => 0,
1168     can_events => 1,
1169     can_hover => 1,
1170 elmex 1.92 tooltip => $resist_names{$_}->[1],
1171 root 1.1 );
1172 root 1.178 $tbl2->add_at ($col + 1, $row, new CFPlus::UI::Image
1173 root 1.1 font => $FONT_FIXED,
1174     can_hover => 1,
1175     can_events => 1,
1176 root 1.78 path => "ui/resist/resist_$_.png",
1177 elmex 1.92 tooltip => $resist_names{$_}->[1],
1178     );
1179 root 1.178 $tbl2->add_at ($col + 2, $row, new CFPlus::UI::Label
1180 elmex 1.92 text => $resist_names{$_}->[0],
1181     font => $FONT_FIXED,
1182     can_hover => 1,
1183     can_events => 1,
1184     tooltip => $resist_names{$_}->[1],
1185 root 1.1 );
1186    
1187     $row++;
1188     if ($row % 6 == 0) {
1189 elmex 1.92 $col += 3;
1190 root 1.1 $row = 0;
1191     }
1192     }
1193    
1194 root 1.95 #update_stats_window ({});
1195 root 1.1
1196 elmex 1.97 $r
1197 root 1.1 }
1198    
1199 elmex 1.92 sub skill_window {
1200 root 1.104 my $sw = new CFPlus::UI::ScrolledWindow (expand => 1);
1201     $sw->add ($STATWIDS->{skill_tbl} = new CFPlus::UI::Table expand => 1, col_expand => [0, 0, 1, 0, 0, 1]);
1202 elmex 1.97 $sw
1203 elmex 1.92 }
1204    
1205 root 1.48 sub formsep($) {
1206     scalar reverse join ",", unpack "(A3)*", reverse $_[0] * 1
1207 root 1.1 }
1208    
1209     my $METASERVER_ATIME;
1210    
1211     sub update_metaserver {
1212 elmex 1.81 my ($metaserver_dialog) = @_;
1213    
1214     $METASERVER = $metaserver_dialog
1215     if defined $metaserver_dialog;
1216    
1217 root 1.1 return if $METASERVER_ATIME > time;
1218     $METASERVER_ATIME = time + 60;
1219    
1220     my $table = $METASERVER->{table};
1221     $table->clear;
1222 root 1.178 $table->add_at (0, 0, my $label = new CFPlus::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
1223 root 1.1
1224 root 1.125 my $ok = 0;
1225 root 1.1
1226 root 1.125 CFPlus::background {
1227     my $ua = CFPlus::lwp_useragent;
1228 root 1.1
1229 root 1.125 CFPlus::background_msg CFPlus::from_json +(CFPlus::lwp_check $ua->get ($META_SERVER))->decoded_content;
1230     } sub {
1231     my ($msg) = @_;
1232     if ($msg) {
1233 root 1.1 $table->clear;
1234    
1235 root 1.62 my @tip = (
1236     "The current number of users logged in on the server.",
1237     "The hostname of the server.",
1238     "The time this server has been running without being restarted.",
1239     "The server software version - a '+' indicates a Crossfire+ server.",
1240     "Short information about this server provided by its admins.",
1241     );
1242     my @col = qw(#Users Host Uptime Version Description);
1243 root 1.178 $table->add_at ($_, 0, new CFPlus::UI::Label
1244 root 1.62 can_hover => 1, can_events => 1,
1245     align => 0, fg => [1, 1, 0],
1246     text => $col[$_], tooltip => $tip[$_])
1247     for 0 .. $#col;
1248 root 1.1
1249     my @align = qw(1 0 1 1 -1);
1250    
1251     my $y = 0;
1252 root 1.142 for my $m (@{ $msg->{servers} }) {
1253     my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime, $highlight) =
1254     @$m{qw(ip age hostname users version description ibytes obytes uptime highlight)};
1255 root 1.1
1256     for ($desc) {
1257     s/<br>/\n/gi;
1258     s/<li>/\n· /gi;
1259     s/<.*?>//sgi;
1260 root 1.125 s/&amp;/&/g;
1261     s/&lt;/</g;
1262     s/&gt;/>/g;
1263 root 1.1 }
1264    
1265     $uptime = sprintf "%dd %02d:%02d:%02d",
1266 root 1.125 (int $uptime / 86400),
1267     (int $uptime / 3600) % 24,
1268     (int $uptime / 60) % 60,
1269     $uptime % 60;
1270 root 1.1
1271     $m = [$users, $host, $uptime, $version, $desc];
1272    
1273     $y++;
1274    
1275 root 1.178 $table->add_at (scalar @$m, $y, new CFPlus::UI::VBox children => [
1276 root 1.104 (new CFPlus::UI::Button
1277 root 1.62 text => "Use",
1278     tooltip => "Put this server into the <b>Host:Port</b> field",
1279     on_activate => sub {
1280 root 1.75 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host);
1281 root 1.62 $METASERVER->hide;
1282 root 1.74 0
1283 root 1.62 },
1284     ),
1285 root 1.104 (new CFPlus::UI::Empty expand => 1),
1286 root 1.1 ]);
1287    
1288 root 1.178 $table->add_at ($_, $y, new CFPlus::UI::Label
1289 root 1.125 max_w => $::WIDTH * 0.4,
1290 root 1.62 ellipsise => 0,
1291     align => $align[$_],
1292     text => $m->[$_],
1293     tooltip => $tip[$_],
1294 root 1.142 fg => ($highlight ? [1, 1, 1] : [.7, .7, .7]),
1295 root 1.62 can_hover => 1,
1296     can_events => 1,
1297     fontsize => 0.8)
1298 root 1.1 for 0 .. $#$m;
1299     }
1300 root 1.125 } else {
1301     $ok or $label->set_text ("error while contacting metaserver");
1302 root 1.1 }
1303 root 1.125 };
1304    
1305 root 1.1 }
1306    
1307 root 1.40 sub metaserver_dialog {
1308 root 1.104 my $vbox = new CFPlus::UI::VBox;
1309     my $table = new CFPlus::UI::Table;
1310     $vbox->add (new CFPlus::UI::ScrolledWindow expand => 1, child => $table);
1311 elmex 1.81
1312 root 1.114 my $dialog = new CFPlus::UI::Toplevel
1313 root 1.62 title => "Server List",
1314     name => 'metaserver_dialog',
1315     x => 'center',
1316     y => 'center',
1317     z => 3,
1318 root 1.125 force_w => $::WIDTH * 0.9,
1319     force_h => $::HEIGHT * 0.7,
1320 elmex 1.81 child => $vbox,
1321 root 1.80 has_close_button => 1,
1322 elmex 1.81 table => $table,
1323 root 1.40 on_visibility_change => sub {
1324 elmex 1.81 update_metaserver ($_[0]) if $_[1];
1325 root 1.74 0
1326 root 1.40 },
1327     ;
1328    
1329     $dialog
1330     }
1331    
1332 root 1.1 sub server_setup {
1333 root 1.104 my $vbox = new CFPlus::UI::VBox;
1334 elmex 1.19
1335 root 1.114 $vbox->add (new CFPlus::UI::FancyFrame
1336     label => "Connection Settings",
1337     child => (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]),
1338     );
1339 root 1.178 $table->add_at (0, 2, new CFPlus::UI::Label valign => 0, align => 1, text => "Host:Port");
1340 root 1.1
1341     {
1342 root 1.178 $table->add_at (1, 2, my $vbox = new CFPlus::UI::VBox);
1343 root 1.1
1344     $vbox->add (
1345 root 1.104 $HOST_ENTRY = new CFPlus::UI::Entry
1346 root 1.1 expand => 1,
1347 root 1.75 text => $CFG->{profile}{default}{host},
1348 root 1.1 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
1349 root 1.18 on_changed => sub {
1350 root 1.1 my ($self, $value) = @_;
1351 root 1.75 $CFG->{profile}{default}{host} = $value;
1352 root 1.192 1
1353 root 1.1 }
1354     );
1355    
1356 root 1.104 $vbox->add (new CFPlus::UI::Button
1357 root 1.40 expand => 1,
1358     text => "Server List",
1359     other => $METASERVER,
1360 root 1.1 tooltip => "Show a list of available crossfire servers",
1361 root 1.74 on_activate => sub { $METASERVER->toggle_visibility; 0 },
1362 root 1.192 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 1 },
1363 root 1.1 );
1364     }
1365    
1366 root 1.178 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username");
1367     $table->add_at (1, 4, new CFPlus::UI::Entry
1368 root 1.75 text => $CFG->{profile}{default}{user},
1369 root 1.1 tooltip => "The name of your character on the server",
1370 root 1.192 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value; 1 }
1371 root 1.1 );
1372    
1373 root 1.178 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password");
1374     $table->add_at (1, 5, new CFPlus::UI::Entry
1375 root 1.75 text => $CFG->{profile}{default}{password},
1376 root 1.1 hidden => 1,
1377     tooltip => "The password for your character",
1378 root 1.192 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value; 1 }
1379 root 1.1 );
1380    
1381 root 1.178 $table->add_at (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size");
1382     $table->add_at (1, 7, new CFPlus::UI::Slider
1383 root 1.30 force_w => 100,
1384 root 1.1 range => [$CFG->{mapsize}, 10, 100, 0, 1],
1385     tooltip => "This is the size of the portion of the map update the server sends you. "
1386     . "If you set this to a high value you will be able to see further, "
1387     . "but you also increase bandwidth requirements and latency. "
1388     . "This option is only used once at log-in.",
1389 root 1.192 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 1 },
1390 root 1.1 );
1391    
1392 root 1.189 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate");
1393     $table->add_at (1, 8, new CFPlus::UI::Entry
1394 root 1.143 text => $CFG->{output_rate},
1395 root 1.192 tooltip => "The maximum bandwidth in bytes per second that the server should not exceed "
1396     . "when sending data. When 0 or unset, the server "
1397     . "default will be used, which is usually around 100kb/s. Most servers will "
1398     . "dynamically find an optimal rate, so adjust this only when necessary.",
1399     on_changed => sub { $CFG->{output_rate} = $_[1]; 1 },
1400 root 1.143 );
1401    
1402 root 1.189 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1403     $table->add_at (1, 9, new CFPlus::UI::Entry
1404 root 1.1 text => $CFG->{output_count},
1405     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1406 root 1.192 on_changed => sub { $CFG->{output_count} = $_[1]; 1 },
1407 root 1.1 );
1408    
1409 root 1.189 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync");
1410     $table->add_at (1, 10, new CFPlus::UI::Entry
1411 root 1.1 text => $CFG->{output_sync},
1412     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1413 root 1.192 on_changed => sub { $CFG->{output_sync} = $_[1]; 1 },
1414 root 1.1 );
1415    
1416 root 1.189 $table->add_at (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button
1417 root 1.1 expand => 1,
1418     align => 0,
1419     text => "Login",
1420 root 1.18 on_activate => sub {
1421 root 1.1 $CONN ? stop_game
1422     : start_game;
1423 root 1.192 1
1424 root 1.1 },
1425     );
1426    
1427 root 1.149 $vbox->add (new CFPlus::UI::FancyFrame
1428     label => "Server Info",
1429     child => ($SERVER_INFO = new CFPlus::UI::Label ellipsise => 0),
1430     );
1431    
1432     $vbox
1433     }
1434    
1435     sub client_setup {
1436     my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1];
1437    
1438     my $row = 0;
1439    
1440 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day");
1441     $table->add_at (1, $row++, new CFPlus::UI::CheckBox
1442 root 1.119 state => $CFG->{show_tips},
1443     tooltip => "Show the <b>Tip of the day</b> window at startup?",
1444     on_changed => sub {
1445     my ($self, $value) = @_;
1446 root 1.121 $CFG->{show_tips} = $value;
1447 root 1.119 0
1448     }
1449     );
1450    
1451 root 1.178 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Messages Window Size");
1452     $table->add_at (1, $row++, my $saycmd = new CFPlus::UI::Entry
1453 root 1.149 text => $CFG->{logview_max_par},
1454     tooltip => "This is maximum number of messages remembered in the <b>Messages</b> window. If the server "
1455     . "sends more messages than this number, older messages get removed to save memory and "
1456     . "computing time. A value of <b>0</b> disables this feature, but that is not recommended.",
1457     on_changed => sub {
1458     my ($self, $value) = @_;
1459 elmex 1.182 $MESSAGE_WINDOW->set_max_para ($CFG->{logview_max_par} = $value*1);
1460 root 1.149 0
1461     },
1462 root 1.67 );
1463    
1464 root 1.149 $table
1465 root 1.1 }
1466    
1467 root 1.49 sub autopickup_setup {
1468 root 1.170 my $r = new CFPlus::UI::ScrolledWindow (
1469     expand => 1,
1470     scroll_y => 1
1471     );
1472     $r->add (my $table = new CFPlus::UI::Table
1473     row_expand => [0],
1474     col_expand => [0, 1, 0, 1],
1475     );
1476 elmex 1.44
1477 elmex 1.43 for (
1478 root 1.51 ["General", 0, 0,
1479 root 1.86 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE],
1480 root 1.58 ["Inhibit autopickup" => PICKUP_INHIBIT],
1481     ["Stop before pickup" => PICKUP_STOP],
1482     ["Debug autopickup" => PICKUP_DEBUG],
1483 root 1.51 ],
1484     ["Weapons", 0, 6,
1485 root 1.58 ["All weapons" => PICKUP_ALLWEAPON],
1486     ["Missile weapons" => PICKUP_MISSILEWEAPON],
1487     ["Bows" => PICKUP_BOW],
1488     ["Arrows" => PICKUP_ARROW],
1489 root 1.51 ],
1490     ["Armour", 0, 12,
1491 root 1.58 ["Helmets" => PICKUP_HELMET],
1492     ["Shields" => PICKUP_SHIELD],
1493     ["Body Armour" => PICKUP_ARMOUR],
1494     ["Boots" => PICKUP_BOOTS],
1495     ["Gloves" => PICKUP_GLOVES],
1496     ["Cloaks" => PICKUP_CLOAK],
1497 root 1.51 ],
1498    
1499 root 1.133 ["Readables", 2, 0,
1500 root 1.58 ["Spellbooks" => PICKUP_SPELLBOOK],
1501     ["Skillscrolls" => PICKUP_SKILLSCROLL],
1502     ["Normal Books/Scrolls" => PICKUP_READABLES],
1503 root 1.51 ],
1504 root 1.133 ["Misc", 2, 5,
1505 root 1.58 ["Food" => PICKUP_FOOD],
1506     ["Drinks" => PICKUP_DRINK],
1507     ["Valuables (Money, Gems)" => PICKUP_VALUABLES],
1508     ["Keys" => PICKUP_KEY],
1509     ["Magical Items" => PICKUP_MAGICAL],
1510     ["Potions" => PICKUP_POTION],
1511     ["Magic Devices" => PICKUP_MAGIC_DEVICE],
1512     ["Ignore cursed" => PICKUP_NOT_CURSED],
1513     ["Jewelery" => PICKUP_JEWELS],
1514 root 1.133 ["Flesh" => PICKUP_FLESH],
1515 root 1.51 ],
1516 elmex 1.66 ["Weight/Value ratio", 2, 17]
1517 elmex 1.43 )
1518     {
1519 root 1.51 my ($title, $x, $y, @bits) = @$_;
1520 root 1.178 $table->add_at ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
1521 root 1.51
1522     for (@bits) {
1523     ++$y;
1524    
1525 elmex 1.43 my $mask = $_->[1];
1526 root 1.178 $table->add_at ($x , $y, new CFPlus::UI::Label text => $_->[0], align => 1, expand => 1);
1527     $table->add_at ($x+1, $y, my $checkbox = new CFPlus::UI::CheckBox
1528 elmex 1.83 state => $::CFG->{pickup} & $mask,
1529 elmex 1.43 on_changed => sub {
1530     my ($box, $value) = @_;
1531 root 1.63
1532 elmex 1.43 if ($value) {
1533 elmex 1.45 $::CFG->{pickup} |= $mask;
1534 elmex 1.43 } else {
1535 root 1.63 $::CFG->{pickup} &= ~$mask;
1536 elmex 1.43 }
1537 root 1.63
1538     $::CONN->send_command ("pickup $::CFG->{pickup}")
1539 elmex 1.45 if defined $::CONN;
1540 root 1.74
1541     0
1542 elmex 1.43 });
1543 root 1.86
1544     ${$_->[2]} = $checkbox if $_->[2];
1545 elmex 1.43 }
1546     }
1547    
1548 root 1.178 $table->add_at (2, 18, new CFPlus::UI::ValSlider
1549 elmex 1.83 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1],
1550     template => ">= 99",
1551 elmex 1.66 to_value => sub { ">= " . 5 * $_[0] },
1552     on_changed => sub {
1553     my ($slider, $value) = @_;
1554    
1555 elmex 1.83 $::CFG->{pickup} &= ~0xF;
1556 elmex 1.66 $::CFG->{pickup} |= int $value
1557     if $value;
1558     1;
1559     });
1560 elmex 1.83
1561 root 1.178 $table->add_at (3, 18, new CFPlus::UI::Button
1562 elmex 1.66 text => "set",
1563     on_activate => sub {
1564     $::CONN->send_command ("pickup $::CFG->{pickup}")
1565     if defined $::CONN;
1566 root 1.74 0
1567 elmex 1.66 });
1568    
1569 root 1.170 $r
1570 elmex 1.43 }
1571    
1572 root 1.102 my %SORT_ORDER = (
1573     type => undef,
1574 root 1.130 mtime => sub {
1575     my $NOW = time;
1576     sort {
1577     my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6;
1578     my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6;
1579    
1580     ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1581     or $btime <=> $atime
1582     or $a->{type} <=> $b->{type}
1583     } @_
1584     },
1585 root 1.102 weight => sub { sort {
1586     $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1587     or $a->{type} <=> $b->{type}
1588     } @_ },
1589     );
1590    
1591 elmex 1.85 sub inventory_widget {
1592 root 1.104 my $hb = new CFPlus::UI::HBox homogeneous => 1;
1593 root 1.1
1594 root 1.104 $hb->add (my $vb1 = new CFPlus::UI::VBox);
1595     $vb1->add (new CFPlus::UI::Label align => 0, text => "Player");
1596 root 1.99
1597 root 1.104 $vb1->add (my $hb1 = new CFPlus::UI::HBox);
1598 root 1.99
1599     use sort 'stable';
1600    
1601 root 1.104 $hb1->add (new CFPlus::UI::Selector
1602 root 1.102 value => $::CFG->{inv_sort},
1603 root 1.99 options => [
1604 root 1.102 [type => "Type/Name"],
1605     [mtime => "Recent/Normal/Locked"],
1606     [weight => "Weight/Type"],
1607 root 1.99 ],
1608     on_changed => sub {
1609 root 1.102 $::CFG->{inv_sort} = $_[1];
1610     $INV->set_sort_order ($SORT_ORDER{$_[1]});
1611 root 1.99 },
1612     );
1613 root 1.104 $hb1->add (new CFPlus::UI::Label text => "Weight: ", align => 1, expand => 1);
1614 root 1.99 #TODO# update to weigh/maxweight
1615 root 1.104 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1);
1616 root 1.99
1617 root 1.104 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1618     $sw1->add ($INV = new CFPlus::UI::Inventory);
1619 root 1.127 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}});
1620 root 1.1
1621 root 1.104 $hb->add (my $vb2 = new CFPlus::UI::VBox);
1622 elmex 1.17
1623 root 1.215 $vb2->add ($INVR_HB = new CFPlus::UI::HBox);
1624 elmex 1.14
1625 root 1.104 $vb2->add (my $sw2 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1626     $sw2->add ($INVR = new CFPlus::UI::Inventory);
1627 root 1.1
1628 elmex 1.27 # XXX: Call after $INVR = ... because set_opencont sets the items
1629 root 1.104 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor");
1630 elmex 1.27
1631 elmex 1.85 $hb
1632 root 1.1 }
1633    
1634 root 1.200 sub media_window {
1635     my $vb = new CFPlus::UI::VBox;
1636    
1637     $vb->add (new CFPlus::UI::FancyFrame
1638     label => "Currently playing music",
1639 root 1.201 child => new CFPlus::UI::ScrolledWindow scroll_x => 1, scroll_y => 0,
1640     child => ($MUSIC_PLAYING_WIDGET = new CFPlus::UI::Label ellipsise => 0, fontsize => 0.8),
1641 root 1.200 );
1642    
1643     $vb->add (new CFPlus::UI::FancyFrame
1644 root 1.201 label => "Other media used in this session",
1645     expand => 1,
1646     child => ($LICENSE_WIDGET = new CFPlus::UI::TextScroller
1647 root 1.200 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4),
1648     );
1649    
1650     $vb
1651     }
1652    
1653 root 1.201 sub add_license {
1654     my ($meta) = @_;
1655    
1656     $meta = $meta->{meta}
1657     or return;
1658    
1659     $meta->{license} || $meta->{author} || $meta->{source}
1660     or return;
1661    
1662     $LICENSE_WIDGET->add_paragraph ({
1663     fg => [1, 1, 1, 1],
1664     markup => "<small>"
1665     . "<b>Name:</b> " . (CFPlus::asxml $meta->{name}) . "\n"
1666     . "<b>Author:</b> " . (CFPlus::asxml $meta->{author}) . "\n"
1667     . "<b>Source:</b> " . (CFPlus::asxml $meta->{source}) . "\n"
1668     . "<b>License:</b> " . (CFPlus::asxml $meta->{license}) . "\n"
1669     . "</small>",
1670     });
1671     $LICENSE_WIDGET->scroll_to_bottom;
1672     }
1673    
1674 root 1.86 sub toggle_player_page {
1675     my ($widget) = @_;
1676    
1677     if ($PL_WINDOW->{visible} && $PL_NOTEBOOK->get_current_page == $widget) {
1678     $PL_WINDOW->hide;
1679     } else {
1680     $PL_NOTEBOOK->set_current_page ($widget);
1681     $PL_WINDOW->show;
1682     }
1683     }
1684    
1685 elmex 1.85 sub player_window {
1686 root 1.114 my $plwin = $PL_WINDOW = new CFPlus::UI::Toplevel
1687 elmex 1.85 x => "center",
1688     y => "center",
1689     force_w => $WIDTH * 9/10,
1690     force_h => $HEIGHT * 9/10,
1691     title => "Player",
1692 elmex 1.90 name => "playerbook",
1693 elmex 1.85 has_close_button => 1
1694     ;
1695    
1696     my $ntb =
1697     $PL_NOTEBOOK =
1698 root 1.157 new CFPlus::UI::Notebook expand => 1;
1699 root 1.86
1700 root 1.184 $ntb->add_tab (
1701 root 1.95 "Statistics (F2)" => $STATS_PAGE = stats_window,
1702 elmex 1.92 "Shows statistics, where all your Stats and Resistances are shown."
1703     );
1704 root 1.184 $ntb->add_tab (
1705 root 1.95 "Skills (F3)" => $SKILL_PAGE = skill_window,
1706 elmex 1.92 "Shows all your Skills."
1707 elmex 1.85 );
1708 elmex 1.97
1709 root 1.131 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1710     $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList);
1711 root 1.184 $ntb->add_tab (
1712 elmex 1.97 "Spellbook (F4)" => $spellsw,
1713 root 1.86 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1714 elmex 1.85 );
1715 root 1.184 $ntb->add_tab (
1716 root 1.95 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1717 root 1.86 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1718     . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1719 elmex 1.85 );
1720 root 1.200 $ntb->add_tab (Pickup => autopickup_setup,
1721 root 1.155 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1722 elmex 1.85
1723 root 1.200 $ntb->add_tab (Media => media_window,
1724     "License, Author and Source info for media sent by the server.");
1725    
1726 root 1.88 $ntb->set_current_page ($INVENTORY_PAGE);
1727 root 1.86
1728 elmex 1.85 $plwin->add ($ntb);
1729     $plwin
1730 elmex 1.38 }
1731    
1732 root 1.49 sub keyboard_setup {
1733 root 1.137 CFPlus::Macro::keyboard_setup
1734 elmex 1.24 }
1735    
1736 root 1.64 sub help_window {
1737 root 1.114 my $win = new CFPlus::UI::Toplevel
1738 root 1.41 x => 'center',
1739     y => 'center',
1740 root 1.119 z => 4,
1741 root 1.41 name => 'doc_browser',
1742     force_w => int $WIDTH * 7/8,
1743     force_h => int $HEIGHT * 7/8,
1744 root 1.87 title => "Help Browser",
1745     has_close_button => 1;
1746 root 1.1
1747 root 1.104 $win->add (my $vbox = new CFPlus::UI::VBox);
1748 root 1.1
1749 root 1.114 $vbox->add (new CFPlus::UI::FancyFrame
1750     label => "Navigation",
1751     child => (my $buttons = new CFPlus::UI::HBox),
1752     );
1753 root 1.104 $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1754 root 1.114 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1755 root 1.1
1756 root 1.111 my @history;
1757     my @future;
1758 root 1.112 my $curnode;
1759 root 1.111
1760     my $load_node; $load_node = sub {
1761 root 1.115 my ($node, $para) = @_;
1762 root 1.111
1763     $buttons->clear;
1764    
1765 root 1.116 $buttons->add (new CFPlus::UI::Button
1766     text => "⇤",
1767     tooltip => "back to the starting page",
1768     on_activate => sub {
1769     unshift @future, [$curnode, $viewer->current_paragraph] if $curnode;
1770     unshift @future, @history;
1771     @history = ();
1772     $load_node->(@{shift @future});
1773     },
1774     );
1775    
1776 root 1.111 if (@history) {
1777 root 1.112 $buttons->add (new CFPlus::UI::Button
1778 root 1.113 text => "⋘",
1779 root 1.116 tooltip => "back to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $history[-1][0]) . "</i>",
1780 root 1.112 on_activate => sub {
1781 root 1.115 unshift @future, [$curnode, $viewer->current_paragraph] if $curnode;
1782     $load_node->(@{pop @history});
1783 root 1.113 },
1784     );
1785 root 1.112 }
1786 root 1.111
1787 root 1.112 if (@future) {
1788     $buttons->add (new CFPlus::UI::Button
1789 root 1.113 text => "â‹™",
1790 root 1.116 tooltip => "forward to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $future[0][0]) . "</i>",
1791 root 1.112 on_activate => sub {
1792 root 1.115 push @history, [$curnode, $viewer->current_paragraph];
1793     $load_node->(@{shift @future});
1794 root 1.113 },
1795     );
1796 root 1.111 }
1797 root 1.112
1798 root 1.113 $buttons->add (new CFPlus::UI::Label text => " ");
1799    
1800     my @path = CFPlus::Pod::full_path_of $node;
1801     pop @path; # drop current node
1802    
1803     for my $node (@path) {
1804     $buttons->add (new CFPlus::UI::Button
1805     text => $node->{kw}[0],
1806     tooltip => "go to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $node) . "</i>",
1807     on_activate => sub {
1808 root 1.115 push @history, [$curnode, $viewer->current_paragraph] if $curnode; @future = ();
1809 root 1.113 $load_node->($node);
1810     },
1811     );
1812     $buttons->add (new CFPlus::UI::Label text => "/");
1813     }
1814    
1815 root 1.114 $buttons->add (new CFPlus::UI::Label text => $node->{kw}[0], padding_x => 4, padding_y => 4);
1816 root 1.113
1817 root 1.112 $curnode = $node;
1818    
1819 root 1.111 $viewer->clear;
1820 root 1.112 $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $curnode);
1821 root 1.115 $viewer->scroll_to ($para);
1822 root 1.111 };
1823    
1824     $load_node->(CFPlus::Pod::find pod => "mainpage");
1825    
1826 root 1.113 $CFPlus::Pod::goto_document = sub {
1827 root 1.111 my (@path) = @_;
1828    
1829 root 1.115 push @history, [$curnode, $viewer->current_paragraph] if $curnode; @future = ();
1830 root 1.111
1831 root 1.116 $load_node->((CFPlus::Pod::find @path)[0]);
1832 root 1.111 $win->show;
1833     };
1834 root 1.1
1835     $win
1836     }
1837    
1838 root 1.119 sub open_string_query {
1839     my ($title, $cb, $txt, $tooltip) = @_;
1840     my $dialog = new CFPlus::UI::Toplevel
1841     x => "center",
1842     y => "center",
1843     z => 50,
1844     force_w => $WIDTH * 4/5,
1845     title => $title;
1846    
1847     $dialog->add (
1848     my $e = new CFPlus::UI::Entry
1849     on_activate => sub { $cb->(@_); $dialog->hide; 0 },
1850     on_key_down => sub { $_[1]->{sym} == 27 and $dialog->hide; 0 },
1851     tooltip => $tooltip
1852     );
1853    
1854     $e->grab_focus;
1855     $e->set_text ($txt) if $txt;
1856     $dialog->show;
1857     }
1858    
1859     sub open_quit_dialog {
1860     unless ($QUIT_DIALOG) {
1861     $QUIT_DIALOG = new CFPlus::UI::Toplevel
1862     x => "center",
1863     y => "center",
1864     z => 50,
1865     title => "Really Quit?",
1866     on_key_down => sub {
1867     my ($dialog, $ev) = @_;
1868     $ev->{sym} == 27 and $dialog->hide;
1869     }
1870     ;
1871    
1872     $QUIT_DIALOG->add (my $vb = new CFPlus::UI::VBox expand => 1);
1873    
1874     $vb->add (new CFPlus::UI::Label
1875     text => "You should find a savebed and apply it first!",
1876     max_w => $WIDTH * 0.25,
1877     ellipsize => 0,
1878     );
1879     $vb->add (my $hb = new CFPlus::UI::HBox expand => 1);
1880     $hb->add (new CFPlus::UI::Button
1881     text => "Ok",
1882     expand => 1,
1883     on_activate => sub { $QUIT_DIALOG->hide; 0 },
1884     );
1885     $hb->add (new CFPlus::UI::Button
1886     text => "Quit anyway",
1887     expand => 1,
1888 root 1.213 on_activate => sub { Event::unloop_all },
1889 root 1.119 );
1890     }
1891    
1892     $QUIT_DIALOG->show;
1893     $QUIT_DIALOG->grab_focus;
1894     }
1895    
1896     sub show_tip_of_the_day {
1897     # find all tips
1898     my @tod = CFPlus::Pod::find tip_of_the_day => "*";
1899    
1900 root 1.148 CFPlus::DB::get state => "tip_of_the_day", sub {
1901     my ($todindex) = @_;
1902     $todindex = 0 if $todindex >= @tod;
1903     CFPlus::DB::put state => tip_of_the_day => $todindex + 1, sub { };
1904    
1905     # create dialog
1906     my $dialog;
1907    
1908     my $close = sub {
1909     $dialog->destroy;
1910     };
1911    
1912     $dialog = new CFPlus::UI::Toplevel
1913     x => "center",
1914     y => "center",
1915     z => 3,
1916     name => 'tip_of_the_day',
1917     force_w => int $WIDTH * 4/9,
1918     force_h => int $WIDTH * 2/9,
1919     title => "Tip of the day #" . (1 + $todindex),
1920     child => my $vbox = new CFPlus::UI::VBox,
1921     has_close_button => 1,
1922     on_delete => $close,
1923     ;
1924    
1925     $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1926     expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1927     $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $tod[$todindex]);
1928    
1929     $vbox->add (my $table = new CFPlus::UI::Table col_expand => [0, 1]);
1930    
1931 root 1.178 $table->add_at (0, 0, new CFPlus::UI::Button
1932 root 1.148 text => "Close",
1933     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>.",
1934     on_activate => $close,
1935     );
1936 root 1.119
1937 root 1.178 $table->add_at (2, 0, new CFPlus::UI::Button
1938 root 1.148 text => "Next",
1939     tooltip => "Show the next <b>Tip of the day</b>.",
1940     on_activate => sub {
1941     $close->();
1942     &show_tip_of_the_day;
1943     },
1944     );
1945 root 1.119
1946 root 1.148 $dialog->show;
1947 root 1.119 };
1948     }
1949    
1950 root 1.1 sub sdl_init {
1951 root 1.104 CFPlus::SDL_Init
1952 root 1.1 and die "SDL::Init failed!\n";
1953     }
1954    
1955     sub video_init {
1956     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1957    
1958     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1959    
1960 root 1.172 ($WIDTH, $HEIGHT, my ($rgb, $alpha)) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1961 root 1.1 $FULLSCREEN = $CFG->{fullscreen};
1962     $FAST = $CFG->{fast};
1963    
1964 root 1.172 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $rgb, $alpha, $FULLSCREEN
1965 root 1.104 or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n";
1966 root 1.1
1967     $SDL_ACTIVE = 1;
1968     $LAST_REFRESH = time - 0.01;
1969    
1970 root 1.104 CFPlus::OpenGL::init;
1971 root 1.1
1972     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1973    
1974 root 1.104 $CFPlus::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1975 root 1.1
1976     #############################################################################
1977    
1978     if ($DEBUG_STATUS) {
1979 root 1.104 CFPlus::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1980 root 1.1 } else {
1981     # create the widgets
1982    
1983 root 1.104 $DEBUG_STATUS = new CFPlus::UI::Label
1984 root 1.30 padding => 0,
1985     z => 100,
1986     force_x => "max",
1987     force_y => 0;
1988 root 1.1 $DEBUG_STATUS->show;
1989 elmex 1.34
1990 root 1.104 $STATUSBOX = new CFPlus::UI::Statusbox;
1991 root 1.54 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1992 root 1.1
1993 root 1.104 (new CFPlus::UI::Frame
1994 root 1.1 bg => [0, 0, 0, 0.4],
1995 root 1.30 force_x => 0,
1996     force_y => "max",
1997 root 1.1 child => $STATUSBOX,
1998     )->show;
1999    
2000 root 1.114 CFPlus::UI::Toplevel->new (
2001 root 1.47 title => "Map",
2002 root 1.42 name => "mapmap",
2003 root 1.30 x => 0,
2004     y => $FONTSIZE + 8,
2005 root 1.1 border_bg => [1, 1, 1, 192/255],
2006     bg => [1, 1, 1, 0],
2007 root 1.104 child => ($MAPMAP = new CFPlus::MapWidget::MapMap
2008 root 1.1 tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
2009     ),
2010     )->show;
2011    
2012 root 1.104 $MAPWIDGET = new CFPlus::MapWidget;
2013 root 1.1 $MAPWIDGET->connect (activate_console => sub {
2014     my ($mapwidget, $preset) = @_;
2015    
2016 elmex 1.182 $MESSAGE_WINDOW->activate_console ($preset)
2017     if $MESSAGE_WINDOW;
2018 root 1.1 });
2019     $MAPWIDGET->show;
2020 root 1.74 $MAPWIDGET->grab_focus;
2021 root 1.1
2022 root 1.114 $SETUP_DIALOG = new CFPlus::UI::Toplevel
2023 root 1.49 title => "Setup",
2024     name => "setup_dialog",
2025     x => 'center',
2026     y => 'center',
2027 root 1.53 z => 2,
2028 root 1.49 force_w => $::WIDTH * 0.6,
2029     force_h => $::HEIGHT * 0.6,
2030 root 1.74 has_close_button => 1,
2031 root 1.49 ;
2032    
2033 elmex 1.81 $METASERVER = metaserver_dialog;
2034 elmex 1.182 $MESSAGE_WINDOW = new CFPlus::UI::MessageWindow;
2035 elmex 1.81
2036 root 1.104 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1,
2037     filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
2038 root 1.49
2039 root 1.184 $SETUP_NOTEBOOK->add_tab (Server => $SETUP_SERVER = server_setup,
2040 root 1.49 "Configure the server to play on, your username, password and other server-related options.");
2041 root 1.184 $SETUP_NOTEBOOK->add_tab (Client => client_setup,
2042 root 1.149 "Configure various client-specific settings.");
2043 root 1.184 $SETUP_NOTEBOOK->add_tab (Graphics => graphics_setup,
2044 root 1.49 "Configure the video mode, performance, fonts and other graphical aspects of the game.");
2045 root 1.184 $SETUP_NOTEBOOK->add_tab (Audio => audio_setup,
2046 root 1.49 "Configure the use of audio, sound effects and background music.");
2047 root 1.184 $SETUP_NOTEBOOK->add_tab (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
2048 root 1.75 "Lets you define, edit and delete key bindings."
2049     . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor "
2050 root 1.49 . "with nothing set and the recording started. After doing the actions you "
2051 root 1.54 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
2052 root 1.49 . "After pressing the combo the binding will be saved automatically and the "
2053     . "binding editor closes");
2054 root 1.184 $SETUP_NOTEBOOK->add_tab (Debug => debug_setup,
2055 root 1.75 "Some debuggin' options. Do not ask.");
2056 root 1.49
2057 root 1.104 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
2058 root 1.1
2059 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
2060 root 1.49 tooltip => "Toggles a dialog where you can configure all aspects of this client.");
2061    
2062 elmex 1.182 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW,
2063 root 1.1 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
2064    
2065     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
2066    
2067 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window,
2068 elmex 1.85 tooltip => "Toggles the player view, where you can manage Inventory, Spells, Skills and see your Stats.");
2069 root 1.1
2070 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Button
2071 root 1.1 text => "Save Config",
2072     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
2073 root 1.18 on_activate => sub {
2074 root 1.104 $::CFG->{layout} = CFPlus::UI::get_layout;
2075     CFPlus::write_cfg "$Crossfire::VARDIR/cfplusrc";
2076 root 1.1 status "Configuration Saved";
2077 root 1.74 0
2078 root 1.1 },
2079     );
2080    
2081 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window,
2082 root 1.1 tooltip => "View Documentation");
2083    
2084 elmex 1.134
2085 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Button
2086 root 1.18 text => "Quit",
2087     tooltip => "Terminates the program",
2088     on_activate => sub {
2089 root 1.1 if ($CONN) {
2090     open_quit_dialog;
2091     } else {
2092 root 1.213 Event::unloop_all;
2093 root 1.1 }
2094 root 1.74 0
2095 root 1.1 },
2096     );
2097    
2098     $BUTTONBAR->show;
2099 root 1.49 $SETUP_DIALOG->show;
2100 elmex 1.214 $MESSAGE_WINDOW->show;
2101 root 1.49 }
2102 root 1.1
2103 root 1.49 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
2104 root 1.1 }
2105    
2106     sub video_shutdown {
2107 root 1.104 CFPlus::OpenGL::shutdown;
2108 root 1.73
2109 root 1.1 undef $SDL_ACTIVE;
2110     }
2111    
2112     my %animate_object;
2113     my $animate_timer;
2114    
2115     my $fps = 9;
2116    
2117     sub force_refresh {
2118 root 1.208 if ($ENV{CFPLUS_DEBUG} & 4) {
2119     $fps = $fps * 0.98 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.02;
2120     debug sprintf "%3.2f", $fps;
2121     }
2122 root 1.1
2123 root 1.104 $CFPlus::UI::ROOT->draw;
2124 root 1.208 CFPlus::SDL_GL_SwapBuffers;
2125 root 1.1 $LAST_REFRESH = $NOW;
2126 root 1.208 $WANT_REFRESH->stop;
2127     }
2128 root 1.1
2129 root 1.208 $WANT_REFRESH = Event->idle (min => 0.001, max => 0.06, parked => 1, cb => \&force_refresh);
2130 root 1.1
2131 root 1.208 my $input = Event->timer (after => 0, hard => 0, interval => 1 / 50, cb => sub {
2132 root 1.1 $NOW = time;
2133    
2134     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
2135 root 1.169 for CFPlus::poll_events;
2136 root 1.1
2137     if (%animate_object) {
2138     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
2139 root 1.208 $WANT_REFRESH->start;
2140 root 1.1 }
2141     });
2142    
2143     sub animation_start {
2144     my ($widget) = @_;
2145     $animate_object{$widget} = $widget;
2146     }
2147    
2148     sub animation_stop {
2149     my ($widget) = @_;
2150     delete $animate_object{$widget};
2151     }
2152    
2153     %SDL_CB = (
2154 root 1.104 CFPlus::SDL_QUIT => sub {
2155 root 1.151 exit;
2156 root 1.1 },
2157 root 1.104 CFPlus::SDL_VIDEORESIZE => sub {
2158 root 1.1 },
2159 root 1.104 CFPlus::SDL_VIDEOEXPOSE => sub {
2160     CFPlus::UI::full_refresh;
2161 root 1.1 },
2162 root 1.104 CFPlus::SDL_ACTIVEEVENT => sub {
2163 root 1.137 # not useful, as APPACTIVE include sonly iconified state, not unmapped
2164 root 1.171 # printf "active %x %x %x\n", $_[0]{gain}, $_[0]{state}, CFPlus::SDL_GetAppState;#d#
2165     # printf "a %x\n", CFPlus::SDL_GetAppState & CFPlus::SDL_APPACTIVE;#d#
2166 root 1.137 # printf "A\n" if $_[0]{state} & CFPlus::SDL_APPACTIVE;
2167     # printf "K\n" if $_[0]{state} & CFPlus::SDL_APPINPUTFOCUS;
2168     # printf "M\n" if $_[0]{state} & CFPlus::SDL_APPMOUSEFOCUS;
2169 root 1.1 },
2170 root 1.104 CFPlus::SDL_KEYDOWN => sub {
2171     if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) {
2172 root 1.1 # alt-enter
2173 root 1.94 $FULLSCREEN_ENABLE->toggle;
2174 root 1.1 video_shutdown;
2175     video_init;
2176     } else {
2177 root 1.104 CFPlus::UI::feed_sdl_key_down_event ($_[0]);
2178 root 1.1 }
2179     },
2180 root 1.104 CFPlus::SDL_KEYUP => \&CFPlus::UI::feed_sdl_key_up_event,
2181     CFPlus::SDL_MOUSEMOTION => \&CFPlus::UI::feed_sdl_motion_event,
2182     CFPlus::SDL_MOUSEBUTTONDOWN => \&CFPlus::UI::feed_sdl_button_down_event,
2183     CFPlus::SDL_MOUSEBUTTONUP => \&CFPlus::UI::feed_sdl_button_up_event,
2184     CFPlus::SDL_USEREVENT => sub {
2185 root 1.1 if ($_[0]{code} == 1) {
2186     audio_channel_finished $_[0]{data1};
2187     } elsif ($_[0]{code} == 0) {
2188     audio_music_finished;
2189     }
2190     },
2191     );
2192    
2193     #############################################################################
2194    
2195     $SIG{INT} = $SIG{TERM} = sub { exit };
2196    
2197     {
2198 root 1.104 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc";
2199 root 1.147 CFPlus::DB::Server::run;
2200    
2201 root 1.104 CFPlus::UI::set_layout ($::CFG->{layout});
2202 root 1.1
2203     my %DEF_CFG = (
2204 root 1.216 sdl_mode => 0,
2205     fullscreen => 0,
2206     fast => 0,
2207 root 1.217 force_opengl11 => undef,
2208 root 1.216 texture_compression => 1,
2209     map_scale => 1,
2210     fow_enable => 1,
2211     fow_intensity => 0,
2212     map_smoothing => 1,
2213     gui_fontsize => 1,
2214     log_fontsize => 0.7,
2215     gauge_fontsize => 1,
2216     gauge_size => 0.35,
2217     stat_fontsize => 0.7,
2218     mapsize => 100,
2219     audio_enable => 1,
2220     effects_enable => 1,
2221     effects_volume => 1,
2222     bgm_enable => 1,
2223     bgm_volume => 0.5,
2224     output_sync => 1,
2225     output_count => 1,
2226     output_rate => "",
2227     pickup => 0,
2228     inv_sort => "mtime",
2229     default => "profile", # default profile
2230     show_tips => 1,
2231     logview_max_par => 1000,
2232 root 1.1 );
2233 root 1.75
2234 root 1.1 while (my ($k, $v) = each %DEF_CFG) {
2235     $CFG->{$k} = $v unless exists $CFG->{$k};
2236     }
2237    
2238 root 1.75 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
2239 root 1.137 $PROFILE = $CFG->{profile}{default};
2240    
2241     # convert old bindings (only default profile matters)
2242     if (my $bindings = delete $PROFILE->{bindings}) {
2243     while (my ($mod, $syms) = each %$bindings) {
2244     while (my ($sym, $cmds) = each %$syms) {
2245     push @{ $PROFILE->{macro} }, {
2246     accelkey => [$mod*1, $sym*1],
2247     action => $cmds,
2248     };
2249     }
2250     }
2251     }
2252 root 1.75
2253 root 1.1 sdl_init;
2254    
2255 root 1.172 @SDL_MODES = CFPlus::SDL_ListModes 8, 8;
2256     @SDL_MODES = CFPlus::SDL_ListModes 5, 0 unless @SDL_MODES;
2257 root 1.104 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
2258 root 1.1
2259 root 1.177 @SDL_MODES = sort { $a->[0] * $a->[1] <=> $b->[0] * $b->[1] } @SDL_MODES;
2260    
2261 root 1.1 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
2262    
2263     {
2264 root 1.104 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw(
2265 root 1.1 DejaVuSans.ttf
2266     DejaVuSansMono.ttf
2267     DejaVuSans-Bold.ttf
2268     DejaVuSansMono-Bold.ttf
2269     DejaVuSans-Oblique.ttf
2270     DejaVuSansMono-Oblique.ttf
2271     DejaVuSans-BoldOblique.ttf
2272     DejaVuSansMono-BoldOblique.ttf
2273     );
2274    
2275 root 1.104 CFPlus::add_font $_ for @fonts;
2276 root 1.1
2277 root 1.104 CFPlus::pango_init;
2278 root 1.1
2279 root 1.104 $FONT_PROP = new_from_file CFPlus::Font $fonts[0];
2280     $FONT_FIXED = new_from_file CFPlus::Font $fonts[1];
2281 root 1.1
2282     $FONT_PROP->make_default;
2283     }
2284    
2285     # compare mono (ft) vs. rgba (cairo)
2286     # ft - 1.8s, cairo 3s, even in alpha-only mode
2287     # for my $rgba (0..1) {
2288     # my $t1 = Time::HiRes::time;
2289     # for (1..1000) {
2290 root 1.104 # my $layout = CFPlus::Layout->new ($rgba);
2291 root 1.1 # $layout->set_text ("hallo" x 100);
2292     # $layout->render;
2293     # }
2294     # my $t2 = Time::HiRes::time;
2295     # warn $t2-$t1;
2296     # }
2297    
2298 root 1.122 $startup_done->();
2299    
2300 root 1.1 video_init;
2301     audio_init;
2302     }
2303    
2304 root 1.119 show_tip_of_the_day if $CFG->{show_tips};
2305    
2306 root 1.1 Event::loop;
2307    
2308 root 1.213 #video_shutdown;
2309     #audio_shutdown;
2310     CFPlus::SDL_Quit;
2311     CFPlus::DB::Server::stop;
2312 root 1.1
2313     =head1 NAME
2314    
2315 root 1.204 cfplus - A Crossfire TRT and Crossfire game client
2316 root 1.1
2317     =head1 SYNOPSIS
2318    
2319     Just run it - no commandline arguments are supported.
2320    
2321     =head1 USAGE
2322    
2323 root 1.204 cfplus utilises OpenGL for all UI elements and the game. It is supposed to
2324     be used in fullscreen mode and interactively.
2325 root 1.1
2326 root 1.39 =head1 DEBUGGING
2327    
2328    
2329     CFPLUS_DEBUG - environment variable
2330    
2331     1 draw borders around widgets
2332     2 add low-level widget info to tooltips
2333     4 show fps
2334     8 suppress tooltips
2335    
2336 root 1.1 =head1 AUTHOR
2337    
2338     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
2339    
2340    
2341