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