ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.159
Committed: Thu Jul 5 06:49:10 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.158: +3 -2 lines
Log Message:
*** empty log message ***

File Contents

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