ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.191
Committed: Sun Jul 29 21:31:48 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.190: +61 -43 lines
Log Message:
finally, primitive server-supplied sound effects

File Contents

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