ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.147
Committed: Fri Apr 6 07:45:35 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.146: +3 -0 lines
Log Message:
- changed most db accesses to be asynchronous
  (this was way more difficult than anticipated)
- face id allocation is still synchronous (and has to be as long as
  we want to allow multiple instances running in parallel)
- facemap is cached locally, so subsequent accesses are fast
- the alternative would be to store faceids, not tileids, in the
  in-memory map, but then mapcache tiles are non-displayable
  unless the server already sent them.
- tile loading and map cache load/store are fully asynchronous
- replaced 32x32 dialogue and noface images by 64 bit versions
- properly scale special dialogue/noface textures to tilesize

File Contents

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