ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.148
Committed: Mon Apr 9 04:39:11 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.147: +45 -43 lines
Log Message:
fix tip of the day database accesses

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