ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.134
Committed: Mon Dec 4 15:17:14 2006 UTC (17 years, 5 months ago) by elmex
Branch: MAIN
Changes since 1.133: +60 -0 lines
Log Message:
added builder ui.

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