ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.142
Committed: Mon Mar 12 22:23:09 2007 UTC (17 years, 2 months ago) by root
Branch: MAIN
Changes since 1.141: +4 -10 lines
Log Message:
rely on server order and hilighting for the metaserver code

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