ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.141
Committed: Sat Dec 16 17:33:26 2006 UTC (17 years, 5 months ago) by elmex
Branch: MAIN
CVS Tags: rel-0_97, rel-2_0, STABLE
Changes since 1.140: +18 -0 lines
Log Message:
fixed a bug with stat swap dialog, where stat upodates come after the query.

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.125 for my $m (
911     sort {
912     $b->{version} <=> $a->{version}
913     or $b->{users} <=> $a->{users}
914     }
915     @{ $msg->{servers} }
916     ) {
917     my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) =
918     @$m{qw(ip age hostname users version description ibytes obytes uptime)};
919 root 1.1
920     for ($desc) {
921     s/<br>/\n/gi;
922     s/<li>/\n· /gi;
923     s/<.*?>//sgi;
924 root 1.125 s/&amp;/&/g;
925     s/&lt;/</g;
926     s/&gt;/>/g;
927 root 1.1 }
928    
929     $uptime = sprintf "%dd %02d:%02d:%02d",
930 root 1.125 (int $uptime / 86400),
931     (int $uptime / 3600) % 24,
932     (int $uptime / 60) % 60,
933     $uptime % 60;
934 root 1.1
935     $m = [$users, $host, $uptime, $version, $desc];
936    
937     $y++;
938    
939 root 1.104 $table->add (scalar @$m, $y, new CFPlus::UI::VBox children => [
940     (new CFPlus::UI::Button
941 root 1.62 text => "Use",
942     tooltip => "Put this server into the <b>Host:Port</b> field",
943     on_activate => sub {
944 root 1.75 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host);
945 root 1.62 $METASERVER->hide;
946 root 1.74 0
947 root 1.62 },
948     ),
949 root 1.104 (new CFPlus::UI::Empty expand => 1),
950 root 1.1 ]);
951    
952 root 1.104 $table->add ($_, $y, new CFPlus::UI::Label
953 root 1.125 max_w => $::WIDTH * 0.4,
954 root 1.62 ellipsise => 0,
955     align => $align[$_],
956     text => $m->[$_],
957     tooltip => $tip[$_],
958 root 1.125 fg => ($m->[3] =~ /\+$/ ? [1, 1, 1] : [.7, .7, .7]),
959 root 1.62 can_hover => 1,
960     can_events => 1,
961     fontsize => 0.8)
962 root 1.1 for 0 .. $#$m;
963     }
964 root 1.125 } else {
965     $ok or $label->set_text ("error while contacting metaserver");
966 root 1.1 }
967 root 1.125 };
968    
969 root 1.1 }
970    
971 root 1.40 sub metaserver_dialog {
972 root 1.104 my $vbox = new CFPlus::UI::VBox;
973     my $table = new CFPlus::UI::Table;
974     $vbox->add (new CFPlus::UI::ScrolledWindow expand => 1, child => $table);
975 elmex 1.81
976 root 1.114 my $dialog = new CFPlus::UI::Toplevel
977 root 1.62 title => "Server List",
978     name => 'metaserver_dialog',
979     x => 'center',
980     y => 'center',
981     z => 3,
982 root 1.125 force_w => $::WIDTH * 0.9,
983     force_h => $::HEIGHT * 0.7,
984 elmex 1.81 child => $vbox,
985 root 1.80 has_close_button => 1,
986 elmex 1.81 table => $table,
987 root 1.40 on_visibility_change => sub {
988 elmex 1.81 update_metaserver ($_[0]) if $_[1];
989 root 1.74 0
990 root 1.40 },
991     ;
992    
993     $dialog
994     }
995    
996 root 1.1 sub server_setup {
997 root 1.104 my $vbox = new CFPlus::UI::VBox;
998 elmex 1.19
999 root 1.114 $vbox->add (new CFPlus::UI::FancyFrame
1000     label => "Connection Settings",
1001     child => (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]),
1002     );
1003 root 1.104 $table->add (0, 2, new CFPlus::UI::Label valign => 0, align => 1, text => "Host:Port");
1004 root 1.1
1005     {
1006 root 1.104 $table->add (1, 2, my $vbox = new CFPlus::UI::VBox);
1007 root 1.1
1008     $vbox->add (
1009 root 1.104 $HOST_ENTRY = new CFPlus::UI::Entry
1010 root 1.1 expand => 1,
1011 root 1.75 text => $CFG->{profile}{default}{host},
1012 root 1.1 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
1013 root 1.18 on_changed => sub {
1014 root 1.1 my ($self, $value) = @_;
1015 root 1.75 $CFG->{profile}{default}{host} = $value;
1016 root 1.74 0
1017 root 1.1 }
1018     );
1019    
1020 root 1.104 $vbox->add (new CFPlus::UI::Button
1021 root 1.40 expand => 1,
1022     text => "Server List",
1023     other => $METASERVER,
1024 root 1.1 tooltip => "Show a list of available crossfire servers",
1025 root 1.74 on_activate => sub { $METASERVER->toggle_visibility; 0 },
1026     on_visibility_change => sub { $METASERVER->hide unless $_[1]; 0 },
1027 root 1.1 );
1028     }
1029    
1030 root 1.104 $table->add (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username");
1031     $table->add (1, 4, new CFPlus::UI::Entry
1032 root 1.75 text => $CFG->{profile}{default}{user},
1033 root 1.1 tooltip => "The name of your character on the server",
1034 root 1.75 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value }
1035 root 1.1 );
1036    
1037 root 1.104 $table->add (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password");
1038     $table->add (1, 5, new CFPlus::UI::Entry
1039 root 1.75 text => $CFG->{profile}{default}{password},
1040 root 1.1 hidden => 1,
1041     tooltip => "The password for your character",
1042 root 1.75 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value }
1043 root 1.1 );
1044    
1045 root 1.104 $table->add (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size");
1046     $table->add (1, 7, new CFPlus::UI::Slider
1047 root 1.30 force_w => 100,
1048 root 1.1 range => [$CFG->{mapsize}, 10, 100, 0, 1],
1049     tooltip => "This is the size of the portion of the map update the server sends you. "
1050     . "If you set this to a high value you will be able to see further, "
1051     . "but you also increase bandwidth requirements and latency. "
1052     . "This option is only used once at log-in.",
1053 root 1.74 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 0 },
1054 root 1.1 );
1055    
1056 root 1.104 $table->add (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Face Prefetch");
1057     $table->add (1, 8, new CFPlus::UI::CheckBox
1058 root 1.1 state => $CFG->{face_prefetch},
1059     tooltip => "<b>Background Image Prefetch</b>\n\n"
1060     . "If enabled, the client automatically pre-fetches images from the server. "
1061     . "This might increase or create lag, but increases the chances "
1062     . "of faces being ready for display when you encounter them. "
1063     . "It also uses up server bandwidth on every connect, "
1064     . "so only set it if you really need to prefetch images. "
1065     . "This option can be set and unset any time.",
1066 root 1.74 on_changed => sub { $CFG->{face_prefetch} = $_[1]; 0 },
1067 root 1.1 );
1068    
1069 root 1.104 $table->add (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1070     $table->add (1, 9, new CFPlus::UI::Entry
1071 root 1.1 text => $CFG->{output_count},
1072     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1073 root 1.74 on_changed => sub { $CFG->{output_count} = $_[1]; 0 },
1074 root 1.1 );
1075    
1076 root 1.104 $table->add (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync");
1077     $table->add (1, 10, new CFPlus::UI::Entry
1078 root 1.1 text => $CFG->{output_sync},
1079     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1080 root 1.74 on_changed => sub { $CFG->{output_sync} = $_[1]; 0 },
1081 root 1.1 );
1082    
1083 root 1.104 $table->add (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button
1084 root 1.1 expand => 1,
1085     align => 0,
1086     text => "Login",
1087 root 1.18 on_activate => sub {
1088 root 1.1 $CONN ? stop_game
1089     : start_game;
1090 root 1.74 0
1091 root 1.1 },
1092     );
1093    
1094 root 1.104 $table->add (0, 12, new CFPlus::UI::Label valign => 0, align => 1, text => "Chat Command");
1095     $table->add (1, 12, my $saycmd = new CFPlus::UI::Entry
1096 root 1.119 text => $CFG->{say_command},
1097 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. "
1098     . "Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
1099     . "But you could also set it to <b>tell <i>playername</i></b> to only chat with that user.",
1100     on_changed => sub {
1101     my ($self, $value) = @_;
1102     $CFG->{say_command} = $value;
1103 root 1.74 0
1104 root 1.49 }
1105     );
1106    
1107 root 1.119 $table->add (0, 13, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day");
1108     $table->add (1, 13, my $saycmd = new CFPlus::UI::CheckBox
1109     state => $CFG->{show_tips},
1110     tooltip => "Show the <b>Tip of the day</b> window at startup?",
1111     on_changed => sub {
1112     my ($self, $value) = @_;
1113 root 1.121 $CFG->{show_tips} = $value;
1114 root 1.119 0
1115     }
1116     );
1117    
1118 root 1.114 $vbox->add (new CFPlus::UI::FancyFrame
1119     label => "Server Info",
1120     child => ($SERVER_INFO = new CFPlus::UI::Label ellipsise => 0),
1121 root 1.67 );
1122    
1123 root 1.49 $vbox
1124 root 1.1 }
1125    
1126     sub message_window {
1127 root 1.114 my $window = new CFPlus::UI::Toplevel
1128 elmex 1.16 name => "message_window",
1129 root 1.1 title => "Messages",
1130     border_bg => [1, 1, 1, 1],
1131 root 1.30 x => "max",
1132     y => 0,
1133 root 1.60 force_w => $::WIDTH * 0.4,
1134     force_h => $::HEIGHT * 0.5,
1135 root 1.104 child => (my $vbox = new CFPlus::UI::VBox),
1136 root 1.74 has_close_button => 1;
1137 root 1.1
1138     $vbox->add ($LOGVIEW);
1139    
1140 root 1.104 $vbox->add (my $input = new CFPlus::UI::Entry
1141 root 1.1 tooltip => "<b>Chat Box</b>. If you enter a text and press return/enter here, the current <i>communication command</i> "
1142     . "from the client setup will be prepended (e.g. <b>shout</b>, <b>chat</b>...). "
1143     . "If you prepend a slash (/), you will submit a command instead (similar to IRC). "
1144     . "A better way to submit commands (and the occasional chat command) is often the map command completer.",
1145 root 1.18 on_focus_in => sub {
1146 root 1.1 my ($input, $prev_focus) = @_;
1147    
1148     delete $input->{refocus_map};
1149    
1150     if ($prev_focus == $MAPWIDGET && $input->{auto_activated}) {
1151     $input->{refocus_map} = 1;
1152     }
1153     delete $input->{auto_activated};
1154 root 1.74
1155     0
1156 root 1.1 },
1157 root 1.18 on_activate => sub {
1158 root 1.1 my ($input, $text) = @_;
1159     $input->set_text ('');
1160    
1161 elmex 1.46 if ($text =~ /^\/(.*)/) {
1162 root 1.1 $::CONN->user_send ($1);
1163     } else {
1164     my $say_cmd = $::CFG->{say_command} || 'say';
1165     $::CONN->user_send ("$say_cmd $text");
1166     }
1167     if ($input->{refocus_map}) {
1168     delete $input->{refocus_map};
1169     $MAPWIDGET->focus_in
1170     }
1171 root 1.74
1172     0
1173 root 1.1 },
1174 root 1.18 on_escape => sub {
1175 root 1.74 $MAPWIDGET->grab_focus;
1176    
1177     0
1178 root 1.1 },
1179     );
1180    
1181     $CONSOLE = {
1182     window => $window,
1183 root 1.30 input => $input,
1184 root 1.1 };
1185    
1186     $window
1187     }
1188    
1189 root 1.49 sub autopickup_setup {
1190 root 1.104 my $table = new CFPlus::UI::Table;
1191 elmex 1.44
1192 elmex 1.43 for (
1193 root 1.51 ["General", 0, 0,
1194 root 1.86 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE],
1195 root 1.58 ["Inhibit autopickup" => PICKUP_INHIBIT],
1196     ["Stop before pickup" => PICKUP_STOP],
1197     ["Debug autopickup" => PICKUP_DEBUG],
1198 root 1.51 ],
1199     ["Weapons", 0, 6,
1200 root 1.58 ["All weapons" => PICKUP_ALLWEAPON],
1201     ["Missile weapons" => PICKUP_MISSILEWEAPON],
1202     ["Bows" => PICKUP_BOW],
1203     ["Arrows" => PICKUP_ARROW],
1204 root 1.51 ],
1205     ["Armour", 0, 12,
1206 root 1.58 ["Helmets" => PICKUP_HELMET],
1207     ["Shields" => PICKUP_SHIELD],
1208     ["Body Armour" => PICKUP_ARMOUR],
1209     ["Boots" => PICKUP_BOOTS],
1210     ["Gloves" => PICKUP_GLOVES],
1211     ["Cloaks" => PICKUP_CLOAK],
1212 root 1.51 ],
1213    
1214 root 1.133 ["Readables", 2, 0,
1215 root 1.58 ["Spellbooks" => PICKUP_SPELLBOOK],
1216     ["Skillscrolls" => PICKUP_SKILLSCROLL],
1217     ["Normal Books/Scrolls" => PICKUP_READABLES],
1218 root 1.51 ],
1219 root 1.133 ["Misc", 2, 5,
1220 root 1.58 ["Food" => PICKUP_FOOD],
1221     ["Drinks" => PICKUP_DRINK],
1222     ["Valuables (Money, Gems)" => PICKUP_VALUABLES],
1223     ["Keys" => PICKUP_KEY],
1224     ["Magical Items" => PICKUP_MAGICAL],
1225     ["Potions" => PICKUP_POTION],
1226     ["Magic Devices" => PICKUP_MAGIC_DEVICE],
1227     ["Ignore cursed" => PICKUP_NOT_CURSED],
1228     ["Jewelery" => PICKUP_JEWELS],
1229 root 1.133 ["Flesh" => PICKUP_FLESH],
1230 root 1.51 ],
1231 elmex 1.66 ["Weight/Value ratio", 2, 17]
1232 elmex 1.43 )
1233     {
1234 root 1.51 my ($title, $x, $y, @bits) = @$_;
1235 root 1.104 $table->add ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
1236 root 1.51
1237     for (@bits) {
1238     ++$y;
1239    
1240 elmex 1.43 my $mask = $_->[1];
1241 root 1.104 $table->add ($x , $y, new CFPlus::UI::Label text => $_->[0], align => 1, expand => 1);
1242     $table->add ($x+1, $y, my $checkbox = new CFPlus::UI::CheckBox
1243 elmex 1.83 state => $::CFG->{pickup} & $mask,
1244 elmex 1.43 on_changed => sub {
1245     my ($box, $value) = @_;
1246 root 1.63
1247 elmex 1.43 if ($value) {
1248 elmex 1.45 $::CFG->{pickup} |= $mask;
1249 elmex 1.43 } else {
1250 root 1.63 $::CFG->{pickup} &= ~$mask;
1251 elmex 1.43 }
1252 root 1.63
1253     $::CONN->send_command ("pickup $::CFG->{pickup}")
1254 elmex 1.45 if defined $::CONN;
1255 root 1.74
1256     0
1257 elmex 1.43 });
1258 root 1.86
1259     ${$_->[2]} = $checkbox if $_->[2];
1260 elmex 1.43 }
1261     }
1262    
1263 root 1.104 $table->add (2, 18, new CFPlus::UI::ValSlider
1264 elmex 1.83 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1],
1265     template => ">= 99",
1266 elmex 1.66 to_value => sub { ">= " . 5 * $_[0] },
1267     on_changed => sub {
1268     my ($slider, $value) = @_;
1269    
1270 elmex 1.83 $::CFG->{pickup} &= ~0xF;
1271 elmex 1.66 $::CFG->{pickup} |= int $value
1272     if $value;
1273     1;
1274     });
1275 elmex 1.83
1276 root 1.104 $table->add (3, 18, new CFPlus::UI::Button
1277 elmex 1.66 text => "set",
1278     on_activate => sub {
1279     $::CONN->send_command ("pickup $::CFG->{pickup}")
1280     if defined $::CONN;
1281 root 1.74 0
1282 elmex 1.66 });
1283    
1284 root 1.51 $table
1285 elmex 1.43 }
1286    
1287 root 1.102 my %SORT_ORDER = (
1288     type => undef,
1289 root 1.130 mtime => sub {
1290     my $NOW = time;
1291     sort {
1292     my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6;
1293     my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6;
1294    
1295     ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1296     or $btime <=> $atime
1297     or $a->{type} <=> $b->{type}
1298     } @_
1299     },
1300 root 1.102 weight => sub { sort {
1301     $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1302     or $a->{type} <=> $b->{type}
1303     } @_ },
1304     );
1305    
1306 elmex 1.85 sub inventory_widget {
1307 root 1.104 my $hb = new CFPlus::UI::HBox homogeneous => 1;
1308 root 1.1
1309 root 1.104 $hb->add (my $vb1 = new CFPlus::UI::VBox);
1310     $vb1->add (new CFPlus::UI::Label align => 0, text => "Player");
1311 root 1.99
1312 root 1.104 $vb1->add (my $hb1 = new CFPlus::UI::HBox);
1313 root 1.99
1314     use sort 'stable';
1315    
1316 root 1.104 $hb1->add (new CFPlus::UI::Selector
1317 root 1.102 value => $::CFG->{inv_sort},
1318 root 1.99 options => [
1319 root 1.102 [type => "Type/Name"],
1320     [mtime => "Recent/Normal/Locked"],
1321     [weight => "Weight/Type"],
1322 root 1.99 ],
1323     on_changed => sub {
1324 root 1.102 $::CFG->{inv_sort} = $_[1];
1325     $INV->set_sort_order ($SORT_ORDER{$_[1]});
1326 root 1.99 },
1327     );
1328 root 1.104 $hb1->add (new CFPlus::UI::Label text => "Weight: ", align => 1, expand => 1);
1329 root 1.99 #TODO# update to weigh/maxweight
1330 root 1.104 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1);
1331 root 1.99
1332 root 1.104 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1333     $sw1->add ($INV = new CFPlus::UI::Inventory);
1334 root 1.127 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}});
1335 root 1.1
1336 root 1.104 $hb->add (my $vb2 = new CFPlus::UI::VBox);
1337 elmex 1.17
1338 root 1.104 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox);
1339 elmex 1.14
1340 root 1.104 $vb2->add (my $sw2 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1341     $sw2->add ($INVR = new CFPlus::UI::Inventory);
1342 root 1.1
1343 elmex 1.27 # XXX: Call after $INVR = ... because set_opencont sets the items
1344 root 1.104 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor");
1345 elmex 1.27
1346 elmex 1.85 $hb
1347 root 1.1 }
1348    
1349 root 1.86 sub toggle_player_page {
1350     my ($widget) = @_;
1351    
1352     if ($PL_WINDOW->{visible} && $PL_NOTEBOOK->get_current_page == $widget) {
1353     $PL_WINDOW->hide;
1354     } else {
1355     $PL_NOTEBOOK->set_current_page ($widget);
1356     $PL_WINDOW->show;
1357     }
1358     }
1359    
1360 elmex 1.85 sub player_window {
1361 root 1.114 my $plwin = $PL_WINDOW = new CFPlus::UI::Toplevel
1362 elmex 1.85 x => "center",
1363     y => "center",
1364     force_w => $WIDTH * 9/10,
1365     force_h => $HEIGHT * 9/10,
1366     title => "Player",
1367 elmex 1.90 name => "playerbook",
1368 elmex 1.85 has_close_button => 1
1369     ;
1370    
1371     my $ntb =
1372     $PL_NOTEBOOK =
1373 root 1.104 new CFPlus::UI::Notebook expand => 1, debug => 1;
1374 root 1.86
1375 elmex 1.85 $ntb->add (
1376 root 1.95 "Statistics (F2)" => $STATS_PAGE = stats_window,
1377 elmex 1.92 "Shows statistics, where all your Stats and Resistances are shown."
1378     );
1379     $ntb->add (
1380 root 1.95 "Skills (F3)" => $SKILL_PAGE = skill_window,
1381 elmex 1.92 "Shows all your Skills."
1382 elmex 1.85 );
1383 elmex 1.97
1384 root 1.131 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1385     $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList);
1386 elmex 1.85 $ntb->add (
1387 elmex 1.97 "Spellbook (F4)" => $spellsw,
1388 root 1.86 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1389 elmex 1.85 );
1390     $ntb->add (
1391 root 1.95 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1392 root 1.86 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1393     . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1394 elmex 1.85 );
1395    
1396 root 1.88 $ntb->set_current_page ($INVENTORY_PAGE);
1397 root 1.86
1398 elmex 1.85 $plwin->add ($ntb);
1399     $plwin
1400 elmex 1.38 }
1401    
1402 root 1.49 sub keyboard_setup {
1403 root 1.137 CFPlus::Macro::keyboard_setup
1404 elmex 1.24 }
1405    
1406 root 1.64 sub help_window {
1407 root 1.114 my $win = new CFPlus::UI::Toplevel
1408 root 1.41 x => 'center',
1409     y => 'center',
1410 root 1.119 z => 4,
1411 root 1.41 name => 'doc_browser',
1412     force_w => int $WIDTH * 7/8,
1413     force_h => int $HEIGHT * 7/8,
1414 root 1.87 title => "Help Browser",
1415     has_close_button => 1;
1416 root 1.1
1417 root 1.104 $win->add (my $vbox = new CFPlus::UI::VBox);
1418 root 1.1
1419 root 1.114 $vbox->add (new CFPlus::UI::FancyFrame
1420     label => "Navigation",
1421     child => (my $buttons = new CFPlus::UI::HBox),
1422     );
1423 root 1.104 $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1424 root 1.114 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1425 root 1.1
1426 root 1.111 my @history;
1427     my @future;
1428 root 1.112 my $curnode;
1429 root 1.111
1430     my $load_node; $load_node = sub {
1431 root 1.115 my ($node, $para) = @_;
1432 root 1.111
1433     $buttons->clear;
1434    
1435 root 1.116 $buttons->add (new CFPlus::UI::Button
1436     text => "⇤",
1437     tooltip => "back to the starting page",
1438     on_activate => sub {
1439     unshift @future, [$curnode, $viewer->current_paragraph] if $curnode;
1440     unshift @future, @history;
1441     @history = ();
1442     $load_node->(@{shift @future});
1443     },
1444     );
1445    
1446 root 1.111 if (@history) {
1447 root 1.112 $buttons->add (new CFPlus::UI::Button
1448 root 1.113 text => "⋘",
1449 root 1.116 tooltip => "back to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $history[-1][0]) . "</i>",
1450 root 1.112 on_activate => sub {
1451 root 1.115 unshift @future, [$curnode, $viewer->current_paragraph] if $curnode;
1452     $load_node->(@{pop @history});
1453 root 1.113 },
1454     );
1455 root 1.112 }
1456 root 1.111
1457 root 1.112 if (@future) {
1458     $buttons->add (new CFPlus::UI::Button
1459 root 1.113 text => "â‹™",
1460 root 1.116 tooltip => "forward to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $future[0][0]) . "</i>",
1461 root 1.112 on_activate => sub {
1462 root 1.115 push @history, [$curnode, $viewer->current_paragraph];
1463     $load_node->(@{shift @future});
1464 root 1.113 },
1465     );
1466 root 1.111 }
1467 root 1.112
1468 root 1.113 $buttons->add (new CFPlus::UI::Label text => " ");
1469    
1470     my @path = CFPlus::Pod::full_path_of $node;
1471     pop @path; # drop current node
1472    
1473     for my $node (@path) {
1474     $buttons->add (new CFPlus::UI::Button
1475     text => $node->{kw}[0],
1476     tooltip => "go to <i>" . (CFPlus::asxml CFPlus::Pod::full_path $node) . "</i>",
1477     on_activate => sub {
1478 root 1.115 push @history, [$curnode, $viewer->current_paragraph] if $curnode; @future = ();
1479 root 1.113 $load_node->($node);
1480     },
1481     );
1482     $buttons->add (new CFPlus::UI::Label text => "/");
1483     }
1484    
1485 root 1.114 $buttons->add (new CFPlus::UI::Label text => $node->{kw}[0], padding_x => 4, padding_y => 4);
1486 root 1.113
1487 root 1.112 $curnode = $node;
1488    
1489 root 1.111 $viewer->clear;
1490 root 1.112 $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $curnode);
1491 root 1.115 $viewer->scroll_to ($para);
1492 root 1.111 };
1493    
1494     $load_node->(CFPlus::Pod::find pod => "mainpage");
1495    
1496 root 1.113 $CFPlus::Pod::goto_document = sub {
1497 root 1.111 my (@path) = @_;
1498    
1499 root 1.115 push @history, [$curnode, $viewer->current_paragraph] if $curnode; @future = ();
1500 root 1.111
1501 root 1.116 $load_node->((CFPlus::Pod::find @path)[0]);
1502 root 1.111 $win->show;
1503     };
1504 root 1.1
1505     $win
1506     }
1507    
1508 root 1.119 sub open_string_query {
1509     my ($title, $cb, $txt, $tooltip) = @_;
1510     my $dialog = new CFPlus::UI::Toplevel
1511     x => "center",
1512     y => "center",
1513     z => 50,
1514     force_w => $WIDTH * 4/5,
1515     title => $title;
1516    
1517     $dialog->add (
1518     my $e = new CFPlus::UI::Entry
1519     on_activate => sub { $cb->(@_); $dialog->hide; 0 },
1520     on_key_down => sub { $_[1]->{sym} == 27 and $dialog->hide; 0 },
1521     tooltip => $tooltip
1522     );
1523    
1524     $e->grab_focus;
1525     $e->set_text ($txt) if $txt;
1526     $dialog->show;
1527     }
1528    
1529     sub open_quit_dialog {
1530     unless ($QUIT_DIALOG) {
1531     $QUIT_DIALOG = new CFPlus::UI::Toplevel
1532     x => "center",
1533     y => "center",
1534     z => 50,
1535     title => "Really Quit?",
1536     on_key_down => sub {
1537     my ($dialog, $ev) = @_;
1538     $ev->{sym} == 27 and $dialog->hide;
1539     }
1540     ;
1541    
1542     $QUIT_DIALOG->add (my $vb = new CFPlus::UI::VBox expand => 1);
1543    
1544     $vb->add (new CFPlus::UI::Label
1545     text => "You should find a savebed and apply it first!",
1546     max_w => $WIDTH * 0.25,
1547     ellipsize => 0,
1548     );
1549     $vb->add (my $hb = new CFPlus::UI::HBox expand => 1);
1550     $hb->add (new CFPlus::UI::Button
1551     text => "Ok",
1552     expand => 1,
1553     on_activate => sub { $QUIT_DIALOG->hide; 0 },
1554     );
1555     $hb->add (new CFPlus::UI::Button
1556     text => "Quit anyway",
1557     expand => 1,
1558     on_activate => sub { exit },
1559     );
1560     }
1561    
1562     $QUIT_DIALOG->show;
1563     $QUIT_DIALOG->grab_focus;
1564     }
1565    
1566     sub show_tip_of_the_day {
1567     # find all tips
1568     my @tod = CFPlus::Pod::find tip_of_the_day => "*";
1569    
1570     my $todindex = $CFPlus::DB_STATE->get ("tip_of_the_day");
1571     $todindex = 0 if $todindex >= @tod;
1572     $CFPlus::DB_STATE->put (tip_of_the_day => $todindex + 1);
1573    
1574     # create dialog
1575     my $dialog;
1576    
1577     my $close = sub {
1578     $dialog->destroy;
1579     };
1580    
1581     $dialog = new CFPlus::UI::Toplevel
1582     x => "center",
1583     y => "center",
1584     z => 3,
1585     name => 'tip_of_the_day',
1586     force_w => int $WIDTH * 4/9,
1587     force_h => int $WIDTH * 2/9,
1588     title => "Tip of the day #" . (1 + $todindex),
1589     child => my $vbox = new CFPlus::UI::VBox,
1590     has_close_button => 1,
1591     on_delete => $close,
1592     ;
1593    
1594     $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1595     expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1596     $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $tod[$todindex]);
1597    
1598 root 1.137 $vbox->add (my $table = new CFPlus::UI::Table col_expand => [0, 1]);
1599 root 1.119
1600     $table->add (0, 0, new CFPlus::UI::Button
1601 root 1.137 text => "Close",
1602 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>.",
1603     on_activate => $close,
1604     );
1605    
1606     $table->add (2, 0, new CFPlus::UI::Button
1607 root 1.137 text => "Next",
1608 root 1.119 tooltip => "Show the next <b>Tip of the day</b>.",
1609     on_activate => sub {
1610     $close->();
1611     &show_tip_of_the_day;
1612     },
1613     );
1614    
1615     $dialog->show;
1616     }
1617    
1618 root 1.1 sub sdl_init {
1619 root 1.104 CFPlus::SDL_Init
1620 root 1.1 and die "SDL::Init failed!\n";
1621     }
1622    
1623     sub video_init {
1624     sdl_init;
1625    
1626     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1627    
1628     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1629    
1630     ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1631     $FULLSCREEN = $CFG->{fullscreen};
1632     $FAST = $CFG->{fast};
1633    
1634 root 1.104 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
1635     or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n";
1636 root 1.1
1637     $SDL_ACTIVE = 1;
1638     $LAST_REFRESH = time - 0.01;
1639    
1640 root 1.104 CFPlus::OpenGL::init;
1641 root 1.1
1642     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1643    
1644 root 1.104 $CFPlus::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1645 root 1.1
1646     #############################################################################
1647    
1648     if ($DEBUG_STATUS) {
1649 root 1.104 CFPlus::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1650 root 1.1 } else {
1651     # create the widgets
1652    
1653 root 1.104 $DEBUG_STATUS = new CFPlus::UI::Label
1654 root 1.30 padding => 0,
1655     z => 100,
1656     force_x => "max",
1657     force_y => 0;
1658 root 1.1 $DEBUG_STATUS->show;
1659 elmex 1.34
1660 root 1.104 $STATUSBOX = new CFPlus::UI::Statusbox;
1661 root 1.54 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1662 root 1.1
1663 root 1.104 (new CFPlus::UI::Frame
1664 root 1.1 bg => [0, 0, 0, 0.4],
1665 root 1.30 force_x => 0,
1666     force_y => "max",
1667 root 1.1 child => $STATUSBOX,
1668     )->show;
1669    
1670 root 1.114 CFPlus::UI::Toplevel->new (
1671 root 1.47 title => "Map",
1672 root 1.42 name => "mapmap",
1673 root 1.30 x => 0,
1674     y => $FONTSIZE + 8,
1675 root 1.1 border_bg => [1, 1, 1, 192/255],
1676     bg => [1, 1, 1, 0],
1677 root 1.104 child => ($MAPMAP = new CFPlus::MapWidget::MapMap
1678 root 1.1 tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
1679     ),
1680     )->show;
1681    
1682 root 1.104 $MAPWIDGET = new CFPlus::MapWidget;
1683 root 1.1 $MAPWIDGET->connect (activate_console => sub {
1684     my ($mapwidget, $preset) = @_;
1685    
1686     if ($CONSOLE) {
1687     $CONSOLE->{input}->{auto_activated} = 1;
1688 root 1.74 $CONSOLE->{input}->grab_focus;
1689 root 1.1
1690     if ($preset && $CONSOLE->{input}->get_text eq '') {
1691     $CONSOLE->{input}->set_text ($preset);
1692     }
1693     }
1694     });
1695     $MAPWIDGET->show;
1696 root 1.74 $MAPWIDGET->grab_focus;
1697 root 1.1
1698 root 1.104 $LOGVIEW = new CFPlus::UI::TextScroller
1699 root 1.1 expand => 1,
1700     font => $FONT_FIXED,
1701     fontsize => $::CFG->{log_fontsize},
1702 root 1.61 indent => -4,
1703 root 1.1 can_hover => 1,
1704     can_events => 1,
1705     tooltip => "<b>Server Log</b>. This text viewer contains all the messages sent by the server.",
1706     ;
1707    
1708 root 1.114 $SETUP_DIALOG = new CFPlus::UI::Toplevel
1709 root 1.49 title => "Setup",
1710     name => "setup_dialog",
1711     x => 'center',
1712     y => 'center',
1713 root 1.53 z => 2,
1714 root 1.49 force_w => $::WIDTH * 0.6,
1715     force_h => $::HEIGHT * 0.6,
1716 root 1.74 has_close_button => 1,
1717 root 1.49 ;
1718    
1719 elmex 1.81 $METASERVER = metaserver_dialog;
1720    
1721 root 1.104 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1,
1722     filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1723 root 1.49
1724     $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup,
1725     "Configure the server to play on, your username, password and other server-related options.");
1726     $SETUP_NOTEBOOK->add (Pickup => autopickup_setup,
1727 root 1.58 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1728 root 1.49 $SETUP_NOTEBOOK->add (Graphics => graphics_setup,
1729     "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1730     $SETUP_NOTEBOOK->add (Audio => audio_setup,
1731     "Configure the use of audio, sound effects and background music.");
1732     $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1733 root 1.75 "Lets you define, edit and delete key bindings."
1734     . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor "
1735 root 1.49 . "with nothing set and the recording started. After doing the actions you "
1736 root 1.54 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
1737 root 1.49 . "After pressing the combo the binding will be saved automatically and the "
1738     . "binding editor closes");
1739 root 1.65 $SETUP_NOTEBOOK->add (Debug => debug_setup,
1740 root 1.75 "Some debuggin' options. Do not ask.");
1741 root 1.49
1742 root 1.104 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1743 root 1.1
1744 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1745 root 1.49 tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1746    
1747 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW = message_window,
1748 root 1.1 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1749    
1750     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
1751    
1752 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window,
1753 elmex 1.85 tooltip => "Toggles the player view, where you can manage Inventory, Spells, Skills and see your Stats.");
1754 root 1.1
1755 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Button
1756 root 1.1 text => "Save Config",
1757     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
1758 root 1.18 on_activate => sub {
1759 root 1.104 $::CFG->{layout} = CFPlus::UI::get_layout;
1760     CFPlus::write_cfg "$Crossfire::VARDIR/cfplusrc";
1761 root 1.1 status "Configuration Saved";
1762 root 1.74 0
1763 root 1.1 },
1764     );
1765    
1766 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window,
1767 root 1.1 tooltip => "View Documentation");
1768    
1769 elmex 1.134
1770 root 1.104 $BUTTONBAR->add (new CFPlus::UI::Button
1771 root 1.18 text => "Quit",
1772     tooltip => "Terminates the program",
1773     on_activate => sub {
1774 root 1.1 if ($CONN) {
1775     open_quit_dialog;
1776     } else {
1777     exit;
1778     }
1779 root 1.74 0
1780 root 1.1 },
1781     );
1782    
1783     $BUTTONBAR->show;
1784 root 1.49 $SETUP_DIALOG->show;
1785     }
1786 root 1.1
1787 root 1.49 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1788 root 1.1 }
1789    
1790 elmex 1.134 sub setup_build_button {
1791     my ($enabled) = @_;
1792     if ($enabled) {
1793 elmex 1.140 $BUILD_BUTTON->hide if $BUILD_BUTTON;
1794 elmex 1.134 $BUILD_BUTTON ||= new CFPlus::UI::Button
1795     text => "Build",
1796     tooltip => "Opens the ingame builder",
1797     on_activate => sub {
1798     if ($CONN) {
1799     $CONN->send_ext_req (builder_player_items => sub {
1800     open_ingame_editor ($_[0]) if exists $_[0]->{items};
1801     });
1802     }
1803     0
1804     };
1805     $BUTTONBAR->add ($BUILD_BUTTON);
1806     } else {
1807     $BUILD_BUTTON->hide if $BUILD_BUTTON;
1808     }
1809     }
1810    
1811     sub open_ingame_editor {
1812     my ($msg) = @_;
1813    
1814     my $win = new CFPlus::UI::Toplevel
1815     x => 0,
1816     y => 'center',
1817     z => 4,
1818     name => 'builder_window',
1819     force_w => int $WIDTH * 1/4,
1820     force_h => int $HEIGHT * 3/4,
1821     title => "In game builder",
1822     has_close_button => 1;
1823    
1824     my $r = new CFPlus::UI::ScrolledWindow (
1825     expand => 1,
1826     scroll_y => 1
1827     );
1828     $r->add (my $vb = new CFPlus::UI::VBox);
1829     $win->add ($r);
1830    
1831    
1832 elmex 1.135 $vb->add (
1833     new CFPlus::UI::Button
1834     text => "Disable build mode",
1835     on_activate => sub { $::IN_BUILD_MODE = undef }
1836     );
1837     $vb->add (
1838     new CFPlus::UI::Button
1839     text => "ERASE",
1840     on_activate => sub { $::IN_BUILD_MODE = { do_erase => 1 } }
1841     );
1842 elmex 1.134
1843 elmex 1.135 for my $itemarchname (
1844     sort {
1845     $msg->{items}->{$a}->{build_arch_name}
1846     cmp $msg->{items}->{$b}->{build_arch_name}
1847     } keys %{$msg->{items}}
1848     ) {
1849     my $info = $msg->{items}->{$itemarchname};
1850 elmex 1.134 $vb->add (
1851 elmex 1.135 new CFPlus::UI::Button text => $info->{build_arch_name},
1852 elmex 1.134 on_activate => sub {
1853 elmex 1.135 $::IN_BUILD_MODE = { item => $itemarchname, info => $info };
1854    
1855     if (grep { $msg->{items}->{$itemarchname}->{$_} } qw/has_connection has_name has_text/) {
1856     build_mode_query_arch_info ();
1857     }
1858 elmex 1.134 }
1859     );
1860     }
1861    
1862     $win->show;
1863     }
1864    
1865 elmex 1.135 sub build_mode_query_arch_info {
1866     my ($iteminfo) = $::IN_BUILD_MODE;
1867     my $itemarchname = $iteminfo->{item};
1868     my $info = $iteminfo->{info};
1869    
1870     my $dialog = new CFPlus::UI::Toplevel
1871     x => "center",
1872     y => "center",
1873     z => 50,
1874     force_w => int $WIDTH * 1/2,
1875     title => "Enter information for placement of '$itemarchname'",
1876     has_close_button => 1;
1877    
1878     $dialog->add (my $vb = new CFPlus::UI::VBox expand => 1);
1879    
1880     $vb->add (my $table = new CFPlus::UI::Table expand => 1);
1881     my $row = 0;
1882     if ($info->{has_name}) {
1883     $table->add (0, $row, new CFPlus::UI::Label text => "Name:");
1884     $table->add (1, $row++, new CFPlus::UI::Entry expand => 1, on_changed => sub { $::IN_BUILD_MODE->{name} = $_[1]; 0 });
1885     }
1886     if ($info->{has_text}) {
1887     $table->add (0, $row, new CFPlus::UI::Label text => "Text:");
1888     $table->add (1, $row++, new CFPlus::UI::Entry expand => 1, on_changed => sub { $::IN_BUILD_MODE->{text} = $_[1]; 0 });
1889     }
1890     if ($info->{has_connection}) {
1891     $table->add (0, $row, new CFPlus::UI::Label text => "Connection ID:");
1892     $table->add (1, $row++,
1893     new CFPlus::UI::Entry
1894     expand => 1,
1895     on_changed => sub { $::IN_BUILD_MODE->{connection} = $_[1]; 0 },
1896     tooltip => "Enter the connection ID here. The connection ID connects actors like a lever to a gate or a magic ear to a gate"
1897     );
1898     }
1899    
1900     $vb->add (my $hb = new CFPlus::UI::HBox expand => 1);
1901     $hb->add (new CFPlus::UI::Button
1902     text => "Close",
1903     expand => 1,
1904     on_activate => sub { $dialog->hide; 0 },
1905     );
1906     $dialog->show;
1907     }
1908    
1909 root 1.1 sub video_shutdown {
1910 root 1.104 CFPlus::OpenGL::shutdown;
1911 root 1.73
1912 root 1.1 undef $SDL_ACTIVE;
1913     }
1914    
1915     my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
1916     my $bgmusic;#TODO#hack#d#
1917    
1918     sub audio_channel_finished {
1919     my ($channel) = @_;
1920    
1921     #warn "channel $channel finished\n";#d#
1922     }
1923    
1924     sub audio_music_finished {
1925     return unless $CFG->{bgm_enable};
1926    
1927     # TODO: hack, do play loop and mood music
1928 root 1.104 $bgmusic = new_from_file CFPlus::MixMusic CFPlus::find_rcfile "music/$bgmusic[0]";
1929 root 1.1 $bgmusic->play (0);
1930    
1931     push @bgmusic, shift @bgmusic;
1932     }
1933    
1934     sub audio_init {
1935     if ($CFG->{audio_enable}) {
1936 root 1.104 if (open my $fh, "<", CFPlus::find_rcfile "sounds/config") {
1937     $SDL_MIXER = !CFPlus::Mix_OpenAudio;
1938 root 1.1
1939     unless ($SDL_MIXER) {
1940     status "Unable to open sound device: there will be no sound";
1941     return;
1942     }
1943    
1944 root 1.104 CFPlus::Mix_AllocateChannels 8;
1945     CFPlus::MixMusic::volume $CFG->{bgm_volume} * 128;
1946 root 1.1
1947     audio_music_finished;
1948    
1949 root 1.129 local $_;
1950 root 1.1 while (<$fh>) {
1951     next if /^\s*#/;
1952     next if /^\s*$/;
1953    
1954     my ($file, $volume, $event) = split /\s+/, $_, 3;
1955    
1956     push @SOUNDS, "$volume,$file";
1957    
1958 root 1.129 $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1959     my $chunk = new_from_file CFPlus::MixChunk CFPlus::find_rcfile "sounds/$file";
1960     $chunk->volume ($volume * 128 / 100);
1961     $chunk
1962     };
1963 root 1.1 }
1964     } else {
1965     status "unable to open sound config: $!";
1966     }
1967     }
1968     }
1969    
1970     sub audio_shutdown {
1971 root 1.104 CFPlus::Mix_CloseAudio if $SDL_MIXER;
1972 root 1.1 undef $SDL_MIXER;
1973     @SOUNDS = ();
1974     %AUDIO_CHUNKS = ();
1975     }
1976    
1977     my %animate_object;
1978     my $animate_timer;
1979    
1980     my $fps = 9;
1981    
1982     my %demo;#d#
1983    
1984     sub force_refresh {
1985     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1986 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1987 root 1.1
1988 root 1.104 $CFPlus::UI::ROOT->draw;
1989 root 1.1
1990     $WANT_REFRESH = 0;
1991     $CAN_REFRESH = 0;
1992     $LAST_REFRESH = $NOW;
1993    
1994 root 1.104 CFPlus::SDL_GL_SwapBuffers;
1995 root 1.1 }
1996    
1997 root 1.49 my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1998 root 1.1 $NOW = time;
1999    
2000     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
2001 root 1.104 for CFPlus::SDL_PollEvent;
2002 root 1.1
2003     if (%animate_object) {
2004     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
2005     $WANT_REFRESH++;
2006     }
2007    
2008     if ($WANT_REFRESH) {
2009     force_refresh;
2010     } else {
2011     $CAN_REFRESH = 1;
2012     }
2013     });
2014    
2015     sub animation_start {
2016     my ($widget) = @_;
2017     $animate_object{$widget} = $widget;
2018     }
2019    
2020     sub animation_stop {
2021     my ($widget) = @_;
2022     delete $animate_object{$widget};
2023     }
2024    
2025     # check once/second for faces that need to be prefetched
2026     # this should, of course, only run on demand, but
2027     # SDL forces worse things on us....
2028    
2029     Event->timer (after => 1, interval => 0.25, cb => sub {
2030     $CONN->face_prefetch
2031     if $CONN;
2032     });
2033    
2034     %SDL_CB = (
2035 root 1.104 CFPlus::SDL_QUIT => sub {
2036 root 1.1 Event::unloop -1;
2037     },
2038 root 1.104 CFPlus::SDL_VIDEORESIZE => sub {
2039 root 1.1 },
2040 root 1.104 CFPlus::SDL_VIDEOEXPOSE => sub {
2041     CFPlus::UI::full_refresh;
2042 root 1.1 },
2043 root 1.104 CFPlus::SDL_ACTIVEEVENT => sub {
2044 root 1.137 # not useful, as APPACTIVE include sonly iconified state, not unmapped
2045     # printf "active %x %x\n", $_[0]{gain}, $_[0]{state};#d#
2046     # printf "A\n" if $_[0]{state} & CFPlus::SDL_APPACTIVE;
2047     # printf "K\n" if $_[0]{state} & CFPlus::SDL_APPINPUTFOCUS;
2048     # printf "M\n" if $_[0]{state} & CFPlus::SDL_APPMOUSEFOCUS;
2049 root 1.1 },
2050 root 1.104 CFPlus::SDL_KEYDOWN => sub {
2051     if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) {
2052 root 1.1 # alt-enter
2053 root 1.94 $FULLSCREEN_ENABLE->toggle;
2054 root 1.1 video_shutdown;
2055     video_init;
2056     } else {
2057 root 1.104 CFPlus::UI::feed_sdl_key_down_event ($_[0]);
2058 root 1.1 }
2059     },
2060 root 1.104 CFPlus::SDL_KEYUP => \&CFPlus::UI::feed_sdl_key_up_event,
2061     CFPlus::SDL_MOUSEMOTION => \&CFPlus::UI::feed_sdl_motion_event,
2062     CFPlus::SDL_MOUSEBUTTONDOWN => \&CFPlus::UI::feed_sdl_button_down_event,
2063     CFPlus::SDL_MOUSEBUTTONUP => \&CFPlus::UI::feed_sdl_button_up_event,
2064     CFPlus::SDL_USEREVENT => sub {
2065 root 1.1 if ($_[0]{code} == 1) {
2066     audio_channel_finished $_[0]{data1};
2067     } elsif ($_[0]{code} == 0) {
2068     audio_music_finished;
2069     }
2070     },
2071     );
2072    
2073     #############################################################################
2074    
2075     $SIG{INT} = $SIG{TERM} = sub { exit };
2076    
2077     {
2078 root 1.104 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc";
2079     CFPlus::UI::set_layout ($::CFG->{layout});
2080 root 1.1
2081     my %DEF_CFG = (
2082 root 1.75 sdl_mode => 0,
2083     width => 640,
2084     height => 480,
2085     fullscreen => 0,
2086     fast => 0,
2087     map_scale => 1,
2088     fow_enable => 1,
2089     fow_intensity => 0.45,
2090     fow_smooth => 0,
2091     gui_fontsize => 1,
2092     log_fontsize => 0.7,
2093     gauge_fontsize => 1,
2094     gauge_size => 0.35,
2095     stat_fontsize => 0.7,
2096     mapsize => 100,
2097 root 1.126 say_command => 'chat',
2098 root 1.75 audio_enable => 1,
2099     bgm_enable => 1,
2100     bgm_volume => 0.25,
2101     face_prefetch => 0,
2102     output_sync => 1,
2103     output_count => 1,
2104     pickup => 0,
2105 root 1.102 inv_sort => "mtime",
2106 root 1.75 default => "profile", # default profile
2107 root 1.119 show_tips => 1,
2108 root 1.1 );
2109 root 1.75
2110 root 1.1 while (my ($k, $v) = each %DEF_CFG) {
2111     $CFG->{$k} = $v unless exists $CFG->{$k};
2112     }
2113    
2114 root 1.75 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
2115 root 1.137 $PROFILE = $CFG->{profile}{default};
2116    
2117     # convert old bindings (only default profile matters)
2118     if (my $bindings = delete $PROFILE->{bindings}) {
2119     while (my ($mod, $syms) = each %$bindings) {
2120     while (my ($sym, $cmds) = each %$syms) {
2121     push @{ $PROFILE->{macro} }, {
2122     accelkey => [$mod*1, $sym*1],
2123     action => $cmds,
2124     };
2125     }
2126     }
2127     }
2128 root 1.75
2129 root 1.1 sdl_init;
2130    
2131     @SDL_MODES = reverse
2132     grep $_->[0] >= 640 && $_->[1] >= 480,
2133 root 1.104 CFPlus::SDL_ListModes;
2134 root 1.1
2135 root 1.104 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
2136 root 1.1
2137     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
2138    
2139     {
2140 root 1.104 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw(
2141 root 1.1 DejaVuSans.ttf
2142     DejaVuSansMono.ttf
2143     DejaVuSans-Bold.ttf
2144     DejaVuSansMono-Bold.ttf
2145     DejaVuSans-Oblique.ttf
2146     DejaVuSansMono-Oblique.ttf
2147     DejaVuSans-BoldOblique.ttf
2148     DejaVuSansMono-BoldOblique.ttf
2149     );
2150    
2151 root 1.104 CFPlus::add_font $_ for @fonts;
2152 root 1.1
2153 root 1.104 CFPlus::pango_init;
2154 root 1.1
2155 root 1.104 $FONT_PROP = new_from_file CFPlus::Font $fonts[0];
2156     $FONT_FIXED = new_from_file CFPlus::Font $fonts[1];
2157 root 1.1
2158     $FONT_PROP->make_default;
2159     }
2160    
2161     # compare mono (ft) vs. rgba (cairo)
2162     # ft - 1.8s, cairo 3s, even in alpha-only mode
2163     # for my $rgba (0..1) {
2164     # my $t1 = Time::HiRes::time;
2165     # for (1..1000) {
2166 root 1.104 # my $layout = CFPlus::Layout->new ($rgba);
2167 root 1.1 # $layout->set_text ("hallo" x 100);
2168     # $layout->render;
2169     # }
2170     # my $t2 = Time::HiRes::time;
2171     # warn $t2-$t1;
2172     # }
2173    
2174 root 1.122 $startup_done->();
2175    
2176 root 1.1 video_init;
2177     audio_init;
2178     }
2179    
2180 root 1.119 show_tip_of_the_day if $CFG->{show_tips};
2181    
2182 root 1.1 Event::loop;
2183 root 1.104 #CFPlus::SDL_Quit;
2184     #CFPlus::_exit 0;
2185 root 1.1
2186 root 1.104 END { CFPlus::SDL_Quit }
2187 root 1.1
2188     =head1 NAME
2189    
2190 root 1.28 cfplus - A Crossfire+ and Crossfire game client
2191 root 1.1
2192     =head1 SYNOPSIS
2193    
2194     Just run it - no commandline arguments are supported.
2195    
2196     =head1 USAGE
2197    
2198 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
2199 root 1.1 fullscreen and interactively.
2200    
2201 root 1.39 =head1 DEBUGGING
2202    
2203    
2204     CFPLUS_DEBUG - environment variable
2205    
2206     1 draw borders around widgets
2207     2 add low-level widget info to tooltips
2208     4 show fps
2209     8 suppress tooltips
2210    
2211 root 1.1 =head1 AUTHOR
2212    
2213     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
2214    
2215    
2216