ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.152
Committed: Tue Apr 24 01:13:15 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.151: +28 -5 lines
Log Message:
make use of new msg command to provide better feedback on character creation: http://data.plan9.de/chargen-nq8.png

File Contents

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