ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.223
Committed: Tue Aug 28 01:23:47 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.222: +22 -2 lines
Log Message:
- generalise keyboard handling a bit into a default
  keymapping + macro functions (such as "!completer shout").
- default keymappings ahve a lower priority, so its possible to
  (in theory) override them. a UI for this has not yet been created.
- re-enable NPOT texture usage, if available.

File Contents

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