ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.136
Committed: Fri Dec 8 14:59:54 2006 UTC (17 years, 5 months ago) by root
Branch: MAIN
Changes since 1.135: +1 -0 lines
Log Message:
implement a somewhat hack multiline text editor

File Contents

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