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