ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.163
Committed: Thu Jul 12 17:56:51 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.162: +56 -8 lines
Log Message:
preliminary per-region music

File Contents

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