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