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