ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.158
Committed: Sun Jul 1 03:12:35 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.157: +1 -1 lines
Log Message:
*** empty log message ***

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