ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.149
Committed: Thu Apr 12 07:53:31 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.148: +94 -55 lines
Log Message:
implement smoothing setting and limit the numbe rof paragraphs in the message window. reorganise setup window a bit

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