ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.57
Committed: Mon Jun 5 21:10:04 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.56: +5 -3 lines
Log Message:
cache data created by parsing pods, as even loading a POM object with storable is extremely slow

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3     use strict;
4     use utf8;
5    
6     # do things only needed for single-binary version (par)
7     BEGIN {
8     if (%PAR::LibCache) {
9     @INC = grep ref, @INC; # weed out all paths except pars loader refs
10    
11     while (my ($filename, $zip) = each %PAR::LibCache) {
12     for ($zip->memberNames) {
13     next unless /^\/root\/(.*)/;
14     $zip->extractMember ($_, "$ENV{PAR_TEMP}/$1")
15     unless -e "$ENV{PAR_TEMP}/$1";
16     }
17     }
18    
19     # TODO: pango-rc file, anybody?
20    
21     unshift @INC, $ENV{PAR_TEMP};
22     }
23     }
24    
25     # need to do it again because that pile of garbage called PAR nukes it before main
26     unshift @INC, $ENV{PAR_TEMP}
27     if %PAR::LibCache;
28    
29     use Time::HiRes 'time';
30     use Event;
31    
32     use Crossfire;
33 root 1.12 use Crossfire::Protocol::Constants;
34 root 1.1
35     use Compress::LZF;
36    
37     use CFClient;
38 root 1.10 use CFClient::OpenGL ();
39 root 1.11 use CFClient::Protocol;
40 root 1.1 use CFClient::UI;
41     use CFClient::MapWidget;
42    
43     $Event::DIED = sub {
44     # TODO: display dialog box or so
45 root 1.49 Carp::confess $_[1];#d#TODO: remove when stable
46 root 1.1 CFClient::error $_[1];
47     };
48    
49     #$SIG{__WARN__} = sub { Carp::cluck $_[0] };#d#
50    
51     our $VERSION = '0.1';
52    
53     my $MAX_FPS = 60;
54     my $MIN_FPS = 5; # unused as of yet
55    
56     our $META_SERVER = "crossfire.real-time.com:13326";
57    
58     our $LAST_REFRESH;
59     our $NOW;
60    
61     our $CFG;
62     our $CONN;
63     our $FAST; # fast, low-quality mode, possibly useful for software-rendering
64    
65     our $WANT_REFRESH;
66     our $CAN_REFRESH;
67    
68     our @SDL_MODES;
69     our $WIDTH;
70     our $HEIGHT;
71     our $FULLSCREEN;
72     our $FONTSIZE;
73    
74     our $FONT_PROP;
75     our $FONT_FIXED;
76    
77     our $MAP;
78     our $MAPMAP;
79     our $MAPWIDGET;
80     our $BUTTONBAR;
81     our $LOGVIEW;
82     our $CONSOLE;
83     our $METASERVER;
84     our $LOGIN_BUTTON;
85     our $QUIT_DIALOG;
86 root 1.40 our $HOST_ENTRY;
87 root 1.49
88     our $SETUP_DIALOG;
89     our $SETUP_NOTEBOOK;
90     our $SETUP_SERVER;
91     our $SETUP_KEYBOARD;
92     our $SETUP_SPELLS;
93 root 1.1
94     our $FLOORBOX;
95     our $GAUGES;
96     our $STATWIDS;
97    
98     our $SDL_ACTIVE;
99     our %SDL_CB;
100    
101     our $SDL_MIXER;
102     our @SOUNDS; # event => file mapping
103     our %AUDIO_CHUNKS; # audio files
104    
105     our $ALT_ENTER_MESSAGE;
106     our $STATUSBOX;
107     our $DEBUG_STATUS;
108    
109 root 1.23 our $INV_WINDOW;
110 root 1.1 our $INV;
111     our $INVR;
112 elmex 1.27 our $INV_RIGHT_HB;
113 root 1.1
114 elmex 1.34 our $BIND_EDITOR;
115 elmex 1.24
116 elmex 1.43 our $PICKUP_CFG;
117 elmex 1.38
118 root 1.1 sub status {
119     $STATUSBOX->add (CFClient::UI::Label::escape $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
120     }
121    
122     sub debug {
123     $DEBUG_STATUS->set_text ($_[0]);
124     }
125    
126     sub start_game {
127     status "logging in...";
128    
129 root 1.23 $LOGIN_BUTTON->set_text ("Logout");
130 root 1.49 $SETUP_DIALOG->hide;
131 root 1.23
132 root 1.1 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
133    
134 root 1.11 my ($host, $port) = split /:/, $CFG->{host};
135    
136 root 1.1 $MAP = new CFClient::Map $mapsize, $mapsize;
137    
138     $CONN = eval {
139 root 1.11 new CFClient::Protocol
140 root 1.1 host => $host,
141     port => $port || 13327,
142     user => $CFG->{user},
143     pass => $CFG->{password},
144     mapw => $mapsize,
145     maph => $mapsize,
146 root 1.11
147     map_widget => $MAPWIDGET,
148     logview => $LOGVIEW,
149     statusbox => $STATUSBOX,
150     map => $MAP,
151     mapmap => $MAPMAP,
152    
153     sound_play => sub {
154     my ($x, $y, $soundnum, $type) = @_;
155    
156     $SDL_MIXER
157     or return;
158    
159     my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
160     or return;
161    
162     $chunk->play;
163     },
164 root 1.1 };
165    
166     if ($CONN) {
167     CFClient::lowdelay fileno $CONN->{fh};
168    
169     status "login successful";
170     } else {
171     status "unable to connect";
172     stop_game();
173     }
174     }
175    
176     sub stop_game {
177 root 1.23 $LOGIN_BUTTON->set_text ("Login");
178 root 1.53 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER);
179 root 1.49 $SETUP_DIALOG->show;
180 root 1.23 $INV_WINDOW->hide;
181    
182 root 1.1 return unless $CONN;
183    
184     status "connection closed";
185 root 1.23
186 root 1.1 $CONN->destroy;
187     $CONN = 0; # false, does not autovivify
188     }
189    
190 root 1.49 sub graphics_setup {
191     my $vbox = new CFClient::UI::VBox;
192 root 1.30
193 root 1.1 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
194    
195     $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode");
196     $table->add (1, 0, my $hbox = new CFClient::UI::HBox);
197    
198 root 1.31 $hbox->add (my $mode_slider = new CFClient::UI::Slider force_w => $WIDTH * 0.1, expand => 1, range => [$CFG->{sdl_mode}, 0, $#SDL_MODES, 0, 1]);
199 root 1.1 $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999");
200    
201     $mode_slider->connect (changed => sub {
202     my ($self, $value) = @_;
203    
204     $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
205     $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]});
206     });
207     $mode_slider->emit (changed => $mode_slider->{range}[0]);
208    
209     my $row = 1;
210    
211     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fullscreen");
212     $table->add (1, $row++, new CFClient::UI::CheckBox
213     state => $CFG->{fullscreen},
214     tooltip => "Bring the client into fullscreen mode.",
215 root 1.18 on_changed => sub {
216 root 1.1 my ($self, $value) = @_;
217     $CFG->{fullscreen} = $value;
218     }
219     );
220    
221     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
222     $table->add (1, $row++, new CFClient::UI::CheckBox
223     state => $CFG->{fast},
224     tooltip => "Lower the visual quality considerably to speed up rendering.",
225 root 1.18 on_changed => sub {
226 root 1.1 my ($self, $value) = @_;
227     $CFG->{fast} = $value;
228     }
229     );
230    
231     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Map Scale");
232     $table->add (1, $row++, new CFClient::UI::Slider
233     range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
234     tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
235 root 1.18 on_changed => sub {
236 root 1.1 my ($self, $value) = @_;
237     $CFG->{map_scale} = 2 ** $value;
238     }
239     );
240    
241     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War");
242     $table->add (1, $row++, new CFClient::UI::CheckBox
243     state => $CFG->{fow_enable},
244     tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.",
245 root 1.18 on_changed => sub {
246 root 1.1 my ($self, $value) = @_;
247     $CFG->{fow_enable} = $value;
248     }
249     );
250    
251     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Intensity");
252     $table->add (1, $row++, new CFClient::UI::Slider
253     range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256],
254     tooltip => "<b>Fog of War Lightness.</b> The higher the intensity, the lighter the Fog-of-War color. Changes are instant.",
255 root 1.18 on_changed => sub {
256 root 1.1 my ($self, $value) = @_;
257     $CFG->{fow_intensity} = $value;
258     }
259     );
260    
261     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Smooth");
262     $table->add (1, $row++, new CFClient::UI::CheckBox
263     state => $CFG->{fow_smooth},
264     tooltip => "Smooth the Fog-of-War a bit to make it more realistic. Changes are instant.",
265 root 1.18 on_changed => sub {
266 root 1.1 my ($self, $value) = @_;
267     $CFG->{fow_smooth} = $value;
268 root 1.15 status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFClient::OpenGL::GL_VERSION < 1.2;
269 root 1.1 }
270     );
271    
272     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
273     $table->add (1, $row++, new CFClient::UI::Slider
274     range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
275     tooltip => "The base font size used by most GUI elements that do not have their own setting.",
276 root 1.18 on_changed => sub { $CFG->{gui_fontsize} = $_[1] },
277 root 1.1 );
278    
279     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Message Fontsize");
280     $table->add (1, $row++, new CFClient::UI::Slider
281     range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1],
282     tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant.",
283 root 1.18 on_changed => sub { $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = $_[1]) },
284 root 1.1 );
285    
286     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Stats Fontsize");
287    
288     $table->add (1, $row++, new CFClient::UI::Slider
289     range => [$CFG->{stat_fontsize}, 0.5, 2, 0, 0.1],
290     tooltip => "The font size used by the <b>statistics window</b> only. Changes are instant.",
291 root 1.18 on_changed => sub {
292 root 1.1 $CFG->{stat_fontsize} = $_[1];
293     &set_stats_window_fontsize;
294     }
295     );
296    
297     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
298     $table->add (1, $row++, new CFClient::UI::Slider
299     range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1],
300     tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.",
301 root 1.18 on_changed => sub {
302 root 1.1 $CFG->{gauge_fontsize} = $_[1];
303     &set_gauge_window_fontsize;
304     }
305     );
306    
307     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size");
308     $table->add (1, $row++, new CFClient::UI::Slider
309 root 1.18 range => [$CFG->{gauge_size}, 0.2, 0.8],
310     tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.",
311     on_changed => sub {
312 root 1.1 $CFG->{gauge_size} = $_[1];
313     $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
314     }
315     );
316    
317     $table->add (1, $row++, new CFClient::UI::Button
318     expand => 1, align => 0, text => "Apply",
319     tooltip => "Apply the video settings",
320 root 1.18 on_activate => sub {
321 root 1.1 video_shutdown ();
322     video_init ();
323     }
324     );
325    
326 root 1.49 $vbox
327     }
328    
329     sub audio_setup {
330     my $vbox = new CFClient::UI::VBox;
331    
332     $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
333    
334     my $row = 0;
335    
336 root 1.1 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Audio Enable");
337     $table->add (1, $row++, new CFClient::UI::CheckBox
338     state => $CFG->{audio_enable},
339     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.",
340 root 1.18 on_changed => sub {
341 root 1.1 $CFG->{audio_enable} = $_[1];
342     }
343     );
344     # $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Effects Volume");
345 root 1.18 # $table->add (1, 8, new CFClient::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
346 root 1.1 # $CFG->{effects_volume} = $_[1];
347     # });
348     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Background Music");
349     $table->add (1, $row++, my $hbox = new CFClient::UI::HBox);
350     $hbox->add (new CFClient::UI::CheckBox
351     expand => 1, state => $CFG->{bgm_enable},
352     tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
353 root 1.18 on_changed => sub {
354 root 1.1 $CFG->{bgm_enable} = $_[1];
355     }
356     );
357     $hbox->add (new CFClient::UI::Slider
358     expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
359     tooltip => "The volume of the background music. Changes are instant.",
360 root 1.18 on_changed => sub {
361 root 1.1 $CFG->{bgm_volume} = $_[1];
362     CFClient::MixMusic::volume $_[1] * 128;
363     }
364     );
365    
366     $table->add (1, $row++, new CFClient::UI::Button
367     expand => 1, align => 0, text => "Apply",
368     tooltip => "Apply the audio settings",
369 root 1.18 on_activate => sub {
370 root 1.1 audio_shutdown ();
371     audio_init ();
372     }
373     );
374    
375 root 1.49 $vbox
376 root 1.1 }
377    
378     sub set_stats_window_fontsize {
379     for (values %{$STATWIDS}) {
380     $_->set_fontsize ($::CFG->{stat_fontsize});
381     }
382     }
383    
384     sub set_gauge_window_fontsize {
385     for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
386     $_->set_fontsize ($::CFG->{gauge_fontsize});
387     }
388     }
389    
390     sub make_gauge_window {
391     my $gh = int $HEIGHT * $CFG->{gauge_size};
392    
393     my $win = new CFClient::UI::Frame (
394 root 1.30 force_x => 0,
395     force_y => "max",
396     force_w => $WIDTH,
397     force_h => $gh,
398 root 1.1 );
399    
400     $win->add (my $hbox = new CFClient::UI::HBox
401     children => [
402     (new CFClient::UI::HBox expand => 1),
403     (new CFClient::UI::VBox children => [
404     (new CFClient::UI::Empty expand => 1),
405 root 1.2 (new CFClient::UI::Frame bg => [0, 0, 0, 0.4], child => ($FLOORBOX = new CFClient::UI::Table)),
406 root 1.1 ]),
407     (my $vbox = new CFClient::UI::VBox),
408     ],
409     );
410    
411     $vbox->add (new CFClient::UI::HBox
412     expand => 1,
413     children => [
414     (new CFClient::UI::Empty expand => 1),
415     (my $hb = new CFClient::UI::HBox),
416     ],
417     );
418    
419     $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp',
420     tooltip => "<b>Health points</b>. Measures of how much damage you can take before dying. Hit points are determined from your level and are influenced by the value of your Con. Hp value may range between 1 to beyond 500 and higher values indicate a greater ability to withstand punishment.");
421     $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana',
422     tooltip => "<b>Spell points</b>. Measures of how much \"fuel\" you have for casting spells and incantations. Mana is calculated from your level and your Pow. Mana values can range between 1 to beyond 500 (glowing crystals can increase the current spell points beyond your normal maximum). Higher values indicate greater amounts of mana.");
423     $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace',
424     tooltip => "<b>Grace points</b> - how favored you are by your god. In game terms, how much divine magic you can cast. Your level, Wis and Pow effect what the value of grace is. Prayong on an altar of your god can increase this value beyond your normal maximum. Grace can take on large positive and negative values. Positive values indicate favor by the gods.");
425     $hb->add (my $fg = new CFClient::UI::Gauge type => 'food',
426     tooltip => "<b>Food</b>. Ranges between 0 (starving) and 999 (satiated). At a value of 0 the character begins to die. Some magic can speed up or slow down the character digestion. Healing wounds will speed up digestion too.");
427    
428     $vbox->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
429     tooltip => "<b>Experience points and overall level</b> - experience is increased as a reward for appropriate action (such as killing monsters) and may decrease as a result of a magical attack or dying. Level is directly derived from the experience value. As the level of the character increases, the character becomes able to succeed at more difficult tasks. A character's level starts at a value of 0 and may range up beyond 100.");
430     $vbox->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
431     tooltip => "<b>Ranged attack</b> - how you attack when you press shift-cursor (spell, skill, weapon etc.)");
432    
433     $GAUGES = {
434     exp => $exp, win => $win, range => $rng,
435     food => $fg, mana => $mg, hp => $hg, grace => $gg
436     };
437    
438     &set_gauge_window_fontsize;
439    
440     $win
441     }
442    
443 elmex 1.24
444 root 1.1 sub make_stats_window {
445 elmex 1.19 my $tgw = new CFClient::UI::FancyFrame
446 root 1.30 y => $HEIGHT * (2/8),
447     x => "max",
448 elmex 1.19 title => "Stats",
449 root 1.30 name => "stats_window";
450 root 1.1
451     $tgw->add (new CFClient::UI::Window child => my $vb = new CFClient::UI::VBox);
452     $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1,
453     can_hover => 1, can_events => 1,
454     tooltip => "Your name and title. You can change your title by using the <b>title</b> command, if supported by the server.");
455     $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1,
456     can_hover => 1, can_events => 1,
457     tooltip => "The map you are currently on (if supported by the server).");
458    
459 elmex 1.5 $vb->add (my $hb0 = new CFClient::UI::HBox);
460     $hb0->add ($STATWIDS->{weight} = new CFClient::UI::Label valign => 0, align => -1, text => "Weight:", expand => 1,
461     can_hover => 1, can_events => 1,
462 root 1.15 tooltip => "The weight of the player including all inventory items.");
463 elmex 1.5 $hb0->add ($STATWIDS->{m_weight} = new CFClient::UI::Label valign => 0, align => -1, text => "Max weight:", expand => 1,
464     can_hover => 1, can_events => 1,
465 root 1.15 tooltip => "The weight limit: you cannot carry more than this.");
466 elmex 1.5
467    
468 root 1.1 $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
469     $hb->add (my $tbl = new CFClient::UI::Table expand => 1);
470    
471     my $color2 = [1, 1, 0];
472    
473     for (
474     [0, 0, st_str => "Str", 30, "<b>Physical Strength</b>, determines damage dealt with weapons, how much you can carry, and how often you can attack"],
475     [0, 1, st_dex => "Dex", 30, "<b>Dexterity</b>, your physical agility. Determines chance of being hit and affects armor class and speed"],
476     [0, 2, st_con => "Con", 30, "<b>Constitution</b>, physical health and toughness. Determines how many healthpoints you can have"],
477     [0, 3, st_int => "Int", 30, "<b>Intelligence</b>, your ability to learn and use skills and incantations (both prayers and magic) and determines how much spell points you can have"],
478     [0, 4, st_wis => "Wis", 30, "<b>Wisdom</b>, the ability to learn and use divine magic (prayers). Determines how many grace points you can have"],
479     [0, 5, st_pow => "Pow", 30, "<b>Power</b>, your magical potential. Influences the strength of spell effects, and also how much your spell and grace points increase when leveling up"],
480     [0, 6, st_cha => "Cha", 30, "<b>Charisma</b>, how well you are received by NPCs. Affects buying and selling prices in shops."],
481    
482     [2, 0, st_wc => "Wc", -120, "<b>Weapon Class</b>, effectiveness of melee/missile attacks. Lower is more potent. Current weapon, level and Str are some things which effect the value of Wc. The value of Wc may range between 25 and -72."],
483     [2, 1, st_ac => "Ac", -120, "<b>Armour Class</b>, how protected you are from being hit by any attack. Lower values are better. Ac is based on your race and is modified by the Dex and current armour worn. For characters that cannot wear armour, Ac improves as their level increases."],
484     [2, 2, st_dam => "Dam", 120, "<b>Damage</b>, how much damage your melee/missile attack inflicts. Higher values indicate a greater amount of damage will be inflicted with each attack."],
485     [2, 3, st_arm => "Arm", 120, "<b>Armour</b>, how much damage (from physical attacks) will be subtracted from successful hits made upon you. This value ranges between 0 to 99%. Current armour worn primarily determines Arm value."],
486     [2, 4, st_spd => "Spd", 10.54, "<b>Speed</b>, how fast you can move. The value of speed may range between nearly 0 (\"very slow\") to higher than 5 (\"lightning fast\"). Base speed is determined from the Dex and modified downward proportionally by the amount of weight carried which exceeds the Max Carry limit. The armour worn also sets the upper limit on speed."],
487     [2, 5, st_wspd => "WSp", 10.54, "<b>Weapon Speed</b>, how many attacks you may make per unit of time (0.120s). Higher values indicate faster attack speed. Current weapon and Dex effect the value of weapon speed."],
488     ) {
489     my ($col, $row, $id, $label, $template, $tooltip) = @$_;
490    
491     $tbl->add ($col , $row, $STATWIDS->{$id} = new CFClient::UI::Label
492     font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0, align => +1, template => $template, tooltip => $tooltip);
493     $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFClient::UI::Label
494     font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0, align => -1, text => $label, tooltip => $tooltip);
495     }
496    
497     $hb->add (my $tbl2 = new CFClient::UI::Table expand => 1);
498    
499     my $row = 0;
500     my $col = 0;
501    
502     my %resist_names = (
503     slow => "<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.)",
504     holyw => "<b>Holy Word</b> (resistance you against getting the fear when someone whose god doesn't like you spells the holy word on you.)",
505     conf => "<b>Confusion</b> (If you are hit by confusion you will move into random directions, and likely into monsters.)",
506     fire => "<b>Fire</b> (just your resistance to fire spells like burning hands, dragonbreath, meteor swarm fire, ...)",
507     depl => "<b>Depletion</b> (some monsters and other effects can cause stats depletion)",
508     magic => "<b>Magic</b> (resistance to magic spells like magic missile or similar)",
509     drain => "<b>Draining</b> (some monsters (e.g. vampires) and other effects can steal experience)",
510     acid => "<b>Acid</b> (resistance to acid, acid hurts pretty much and also corrodes your weapons)",
511     pois => "<b>Poison</b> (resistance to getting poisoned)",
512     para => "<b>Paralysation</b> (this resistance affects the chance you get paralysed)",
513     deat => "<b>Death</b> (resistance against death spells)",
514     phys => "<b>Physical</b> (this is the resistance against physical attacks, like when a monster hit you in melee combat)",
515     blind => "<b>Blind</b> (blind resistance affects the chance of a successful blinding attack)",
516     fear => "<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)",
517     tund => "<b>Turn undead</b> (affects your resistancy to various forms of 'turn undead' spells. Only relevant when you are, in fact, undead...",
518     elec => "<b>Electricity</b> (resistance against electricity, spells like large lightning, small lightning, ...)",
519     cold => "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)",
520     ghit => "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)",
521     );
522     for (qw/slow holyw conf fire depl magic
523     drain acid pois para deat phys
524     blind fear tund elec cold ghit/)
525     {
526     $tbl2->add ($col, $row,
527     $STATWIDS->{"res_$_"} =
528     new CFClient::UI::Label
529     font => $FONT_FIXED,
530     template => "-100%",
531     align => +1,
532     valign => 0,
533     can_events => 1,
534     can_hover => 1,
535     tooltip => $resist_names{$_},
536     );
537     $tbl2->add ($col + 1, $row, new CFClient::UI::Image
538     font => $FONT_FIXED,
539     can_hover => 1,
540     can_events => 1,
541     image => "ui/resist/resist_$_.png",
542     tooltip => $resist_names{$_},
543     );
544    
545     $row++;
546     if ($row % 6 == 0) {
547     $col += 2;
548     $row = 0;
549     }
550     }
551    
552     &set_stats_window_fontsize;
553     update_stats_window ({});
554    
555     $tgw
556     }
557    
558 root 1.48 sub formsep($) {
559     scalar reverse join ",", unpack "(A3)*", reverse $_[0] * 1
560 root 1.1 }
561    
562     sub update_stats_window {
563     my ($stats) = @_;
564    
565 root 1.12 # I love text protocols...
566    
567     my $hp = $stats->{+CS_STAT_HP} * 1;
568     my $hp_m = $stats->{+CS_STAT_MAXHP} * 1;
569     my $sp = $stats->{+CS_STAT_SP} * 1;
570     my $sp_m = $stats->{+CS_STAT_MAXSP} * 1;
571     my $fo = $stats->{+CS_STAT_FOOD} * 1;
572 root 1.1 my $fo_m = 999;
573 root 1.12 my $gr = $stats->{+CS_STAT_GRACE} * 1;
574     my $gr_m = $stats->{+CS_STAT_MAXGRACE} * 1;
575 root 1.1
576     $GAUGES->{hp} ->set_value ($hp, $hp_m);
577     $GAUGES->{mana} ->set_value ($sp, $sp_m);
578     $GAUGES->{food} ->set_value ($fo, $fo_m);
579     $GAUGES->{grace} ->set_value ($gr, $gr_m);
580 root 1.12 $GAUGES->{exp} ->set_text ("Exp: " . (formsep $stats->{+CS_STAT_EXP64})
581     . " (lvl " . ($stats->{+CS_STAT_LEVEL} * 1) . ")");
582     my $rng = $stats->{+CS_STAT_RANGE};
583 root 1.1 $rng =~ s/^Range: //; # thank you so much dear server
584     $GAUGES->{range} ->set_text ("Rng: " . $rng);
585 root 1.12 my $title = $stats->{+CS_STAT_TITLE};
586 root 1.1 $title =~ s/^Player: //;
587     $STATWIDS->{title} ->set_text ("Title: " . $title);
588    
589 root 1.12 $STATWIDS->{st_str} ->set_text (sprintf "%d" , $stats->{+CS_STAT_STR});
590     $STATWIDS->{st_dex} ->set_text (sprintf "%d" , $stats->{+CS_STAT_DEX});
591     $STATWIDS->{st_con} ->set_text (sprintf "%d" , $stats->{+CS_STAT_CON});
592     $STATWIDS->{st_int} ->set_text (sprintf "%d" , $stats->{+CS_STAT_INT});
593     $STATWIDS->{st_wis} ->set_text (sprintf "%d" , $stats->{+CS_STAT_WIS});
594     $STATWIDS->{st_pow} ->set_text (sprintf "%d" , $stats->{+CS_STAT_POW});
595     $STATWIDS->{st_cha} ->set_text (sprintf "%d" , $stats->{+CS_STAT_CHA});
596     $STATWIDS->{st_wc} ->set_text (sprintf "%d" , $stats->{+CS_STAT_WC});
597     $STATWIDS->{st_ac} ->set_text (sprintf "%d" , $stats->{+CS_STAT_AC});
598     $STATWIDS->{st_dam} ->set_text (sprintf "%d" , $stats->{+CS_STAT_DAM});
599     $STATWIDS->{st_arm} ->set_text (sprintf "%d" , $stats->{+CS_STAT_ARMOUR});
600     $STATWIDS->{st_spd} ->set_text (sprintf "%.1f", $stats->{+CS_STAT_SPEED});
601     $STATWIDS->{st_wspd}->set_text (sprintf "%.1f", $stats->{+CS_STAT_WEAP_SP});
602 root 1.1
603 root 1.12 $STATWIDS->{m_weight}->set_text (sprintf "Max weight: %.1fkg", $stats->{+CS_STAT_WEIGHT_LIM} / 1000);
604 elmex 1.5
605 root 1.12 # TODO: replace by CS_STAT_RES_xxx constants
606 root 1.1 my %tbl = (
607     phys => 100,
608     magic => 101,
609     fire => 102,
610     elec => 103,
611     cold => 104,
612     conf => 105,
613     acid => 106,
614     drain => 107,
615     ghit => 108,
616     pois => 109,
617     slow => 110,
618     para => 111,
619     tund => 112,
620     fear => 113,
621     depl => 113,
622     deat => 115,
623     holyw => 116,
624 root 1.12 blind => 117,
625 root 1.1 );
626    
627 root 1.12 $STATWIDS->{"res_$_"}->set_text (sprintf "%d%", $stats->{$tbl{$_}})
628     for keys %tbl;
629 root 1.1 }
630    
631     my $METASERVER_ATIME;
632    
633     sub update_metaserver {
634     return if $METASERVER_ATIME > time;
635     $METASERVER_ATIME = time + 60;
636    
637     my $table = $METASERVER->{table};
638     $table->clear;
639     $table->add (0, 0, my $label = new CFClient::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
640    
641     my $buf;
642    
643     my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0;
644    
645     unless ($fh) {
646     $label->set_text ("unable to contact metaserver: $!");
647     return;
648     }
649    
650     Event->io (fd => $fh, poll => 'r', cb => sub {
651     my $res = sysread $fh, $buf, 8192, length $buf;
652    
653     if (!defined $res) {
654     $_[0]->w->cancel;
655     $label->set_text ("error while retrieving server list: $!");
656     } elsif ($res == 0) {
657     $_[0]->w->cancel;
658     status "server list retrieved";
659    
660     utf8::decode $buf if utf8::valid $buf;
661    
662     $table->clear;
663    
664     my @col = qw(Use #Users Host Uptime Version Description);
665     $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_])
666     for 0 .. $#col;
667    
668     my @align = qw(1 0 1 1 -1);
669    
670     my $y = 0;
671     for my $m (sort { $b->[3] <=> $a->[3] } map [split /\|/], split /\015?\012/, $buf) {
672     my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) = @$m;
673    
674     for ($desc) {
675     s/<br>/\n/gi;
676     s/<li>/\n· /gi;
677     s/<.*?>//sgi;
678     s/&/&amp;/g;
679     s/</&lt;/g;
680     s/>/&gt;/g;
681     }
682    
683     $uptime = sprintf "%dd %02d:%02d:%02d",
684     (int $m->[8] / 86400),
685     (int $m->[8] / 3600) % 24,
686     (int $m->[8] / 60) % 60,
687     $m->[8] % 60;
688    
689     $m = [$users, $host, $uptime, $version, $desc];
690    
691     $y++;
692    
693     $table->add (0, $y, new CFClient::UI::VBox children => [
694 root 1.18 (new CFClient::UI::Button text => "Use", on_activate => sub {
695 root 1.40 $HOST_ENTRY->set_text ($CFG->{host} = $host);
696     $METASERVER->toggle_visibility;
697 root 1.1 }),
698     (new CFClient::UI::Empty expand => 1),
699     ]);
700    
701     $table->add ($_ + 1, $y, new CFClient::UI::Label
702     ellipsise => 0, align => $align[$_], text => $m->[$_], fontsize => 0.8)
703     for 0 .. $#$m;
704     }
705     }
706     });
707     }
708    
709 root 1.40 sub metaserver_dialog {
710     my $dialog = new CFClient::UI::FancyFrame
711     title => "Server List",
712 root 1.41 name => 'metaserver_dialog',
713 root 1.40 x => 'center',
714     y => 'center',
715 root 1.57 z => 3,
716 root 1.40 child => (my $vbox = new CFClient::UI::VBox),
717     on_visibility_change => sub {
718     update_metaserver if $_[1];
719     },
720     ;
721    
722     $vbox->add ($dialog->{table} = new CFClient::UI::Table);
723    
724     $dialog
725     }
726    
727 root 1.1 sub server_setup {
728 root 1.49 my $vbox = new CFClient::UI::VBox;
729 elmex 1.19
730 root 1.1 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
731     $table->add (0, 2, new CFClient::UI::Label valign => 0, align => 1, text => "Host:Port");
732    
733     {
734     $table->add (1, 2, my $vbox = new CFClient::UI::VBox);
735    
736     $vbox->add (
737 root 1.40 $HOST_ENTRY = new CFClient::UI::Entry
738 root 1.1 expand => 1,
739     text => $CFG->{host},
740     tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
741 root 1.18 on_changed => sub {
742 root 1.1 my ($self, $value) = @_;
743     $CFG->{host} = $value;
744     }
745     );
746    
747     $METASERVER = metaserver_dialog;
748    
749 root 1.40 $vbox->add (new CFClient::UI::Button
750     expand => 1,
751     text => "Server List",
752     other => $METASERVER,
753 root 1.1 tooltip => "Show a list of available crossfire servers",
754 root 1.40 on_activate => sub { $METASERVER->toggle_visibility },
755 root 1.1 );
756     }
757    
758     $table->add (0, 4, new CFClient::UI::Label valign => 0, align => 1, text => "Username");
759     $table->add (1, 4, new CFClient::UI::Entry
760     text => $CFG->{user},
761     tooltip => "The name of your character on the server",
762 root 1.18 on_changed => sub {
763 root 1.1 my ($self, $value) = @_;
764     $CFG->{user} = $value;
765     }
766     );
767    
768     $table->add (0, 5, new CFClient::UI::Label valign => 0, align => 1, text => "Password");
769     $table->add (1, 5, new CFClient::UI::Entry
770     text => $CFG->{password},
771     hidden => 1,
772     tooltip => "The password for your character",
773 root 1.18 on_changed => sub {
774 root 1.1 my ($self, $value) = @_;
775     $CFG->{password} = $value;
776     }
777     );
778    
779     $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size");
780     $table->add (1, 7, new CFClient::UI::Slider
781 root 1.30 force_w => 100,
782 root 1.1 range => [$CFG->{mapsize}, 10, 100, 0, 1],
783     tooltip => "This is the size of the portion of the map update the server sends you. "
784     . "If you set this to a high value you will be able to see further, "
785     . "but you also increase bandwidth requirements and latency. "
786     . "This option is only used once at log-in.",
787 root 1.18 on_changed => sub {
788 root 1.1 my ($self, $value) = @_;
789    
790     $CFG->{mapsize} = $self->{range}[0] = $value = int $value;
791     },
792     );
793    
794     $table->add (0, 8, new CFClient::UI::Label valign => 0, align => 1, text => "Face Prefetch");
795     $table->add (1, 8, new CFClient::UI::CheckBox
796     state => $CFG->{face_prefetch},
797     tooltip => "<b>Background Image Prefetch</b>\n\n"
798     . "If enabled, the client automatically pre-fetches images from the server. "
799     . "This might increase or create lag, but increases the chances "
800     . "of faces being ready for display when you encounter them. "
801     . "It also uses up server bandwidth on every connect, "
802     . "so only set it if you really need to prefetch images. "
803     . "This option can be set and unset any time.",
804 root 1.18 on_changed => sub { $CFG->{face_prefetch} = $_[1] },
805 root 1.1 );
806    
807     $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Output-Count");
808     $table->add (1, 9, new CFClient::UI::Entry
809     text => $CFG->{output_count},
810     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
811 root 1.18 on_changed => sub { $CFG->{output_count} = $_[1] },
812 root 1.1 );
813    
814     $table->add (0, 10, new CFClient::UI::Label valign => 0, align => 1, text => "Output-Sync");
815     $table->add (1, 10, new CFClient::UI::Entry
816     text => $CFG->{output_sync},
817     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
818 root 1.18 on_changed => sub { $CFG->{output_sync} = $_[1] },
819 root 1.1 );
820    
821     $table->add (1, 11, $LOGIN_BUTTON = new CFClient::UI::Button
822     expand => 1,
823     align => 0,
824     text => "Login",
825 root 1.18 on_activate => sub {
826 root 1.1 $CONN ? stop_game
827     : start_game;
828     },
829     );
830    
831 root 1.49 $table->add (0, 12, new CFClient::UI::Label valign => 0, align => 1, text => "Chat Command");
832     $table->add (1, 12, my $saycmd = new CFClient::UI::Entry
833     text => $CFG->{say_command},
834     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. "
835     . "Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
836     . "But you could also set it to <b>tell <i>playername</i></b> to only chat with that user.",
837     on_changed => sub {
838     my ($self, $value) = @_;
839     $CFG->{say_command} = $value;
840     }
841     );
842    
843     $vbox
844 root 1.1 }
845    
846     sub message_window {
847     my $window = new CFClient::UI::FancyFrame
848 elmex 1.16 name => "message_window",
849 root 1.1 title => "Messages",
850     border_bg => [1, 1, 1, 1],
851     bg => [0, 0, 0, 0.75],
852 root 1.30 x => "max",
853     y => 0,
854     force_w => $::WIDTH / 3,
855     force_h => $::HEIGHT / 5,
856 root 1.1 child => (my $vbox = new CFClient::UI::VBox);
857    
858     $vbox->add ($LOGVIEW);
859    
860     $vbox->add (my $input = new CFClient::UI::Entry
861     tooltip => "<b>Chat Box</b>. If you enter a text and press return/enter here, the current <i>communication command</i> "
862     . "from the client setup will be prepended (e.g. <b>shout</b>, <b>chat</b>...). "
863     . "If you prepend a slash (/), you will submit a command instead (similar to IRC). "
864     . "A better way to submit commands (and the occasional chat command) is often the map command completer.",
865 root 1.18 on_focus_in => sub {
866 root 1.1 my ($input, $prev_focus) = @_;
867    
868     delete $input->{refocus_map};
869    
870     if ($prev_focus == $MAPWIDGET && $input->{auto_activated}) {
871     $input->{refocus_map} = 1;
872     }
873     delete $input->{auto_activated};
874     },
875 root 1.18 on_activate => sub {
876 root 1.1 my ($input, $text) = @_;
877     $input->set_text ('');
878    
879 elmex 1.46 if ($text =~ /^\/(.*)/) {
880 root 1.1 $::CONN->user_send ($1);
881     } else {
882     my $say_cmd = $::CFG->{say_command} || 'say';
883     $::CONN->user_send ("$say_cmd $text");
884     }
885     if ($input->{refocus_map}) {
886     delete $input->{refocus_map};
887     $MAPWIDGET->focus_in
888     }
889     },
890 root 1.18 on_escape => sub {
891 root 1.1 $MAPWIDGET->focus_in
892     },
893     );
894    
895     $CONSOLE = {
896     window => $window,
897 root 1.30 input => $input,
898 root 1.1 };
899    
900     $window
901     }
902    
903     sub open_quit_dialog {
904     unless ($QUIT_DIALOG) {
905 root 1.30 $QUIT_DIALOG = new CFClient::UI::FancyFrame
906     x => "center",
907     y => "center",
908 root 1.55 z => 50,
909 root 1.30 title => "Really Quit?",
910     ;
911 root 1.1
912     $QUIT_DIALOG->add (my $vb = new CFClient::UI::VBox expand => 1);
913    
914     $vb->add (new CFClient::UI::Label
915     text => "You should find a savebed and apply it first!",
916     max_w => $WIDTH * 0.25,
917     ellipsize => 0,
918     );
919     $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
920     $hb->add (new CFClient::UI::Button
921     text => "Ok",
922     expand => 1,
923 root 1.18 on_activate => sub { $QUIT_DIALOG->hide },
924 root 1.1 );
925     $hb->add (new CFClient::UI::Button
926     text => "Quit anyway",
927     expand => 1,
928 root 1.18 on_activate => sub { exit },
929 root 1.1 );
930 root 1.21 }
931 root 1.1
932 root 1.21 $QUIT_DIALOG->show;
933 root 1.1 }
934    
935 root 1.49 sub autopickup_setup {
936 root 1.51 my $table = new CFClient::UI::Table;
937 elmex 1.44
938 elmex 1.43 for (
939 root 1.51 ["General", 0, 0,
940     ["Enable autopickup" => CFClient::Pickup::PU_NEWMODE],
941     ["Inhibit autopickup" => CFClient::Pickup::PU_INHIBIT],
942     ["Stop before pickup" => CFClient::Pickup::PU_STOP],
943     ["Debug autopickup" => CFClient::Pickup::PU_DEBUG],
944     ],
945     ["Weapons", 0, 6,
946     ["All weapons" => CFClient::Pickup::PU_ALLWEAPON],
947     ["Missile weapons" => CFClient::Pickup::PU_MISSILEWEAPON],
948     ["Bows" => CFClient::Pickup::PU_BOW],
949     ["Arrows" => CFClient::Pickup::PU_ARROW],
950     ],
951     ["Armour", 0, 12,
952     ["Helmets" => CFClient::Pickup::PU_HELMET],
953     ["Shields" => CFClient::Pickup::PU_SHIELD],
954     ["Body Armour" => CFClient::Pickup::PU_ARMOUR],
955     ["Boots" => CFClient::Pickup::PU_BOOTS],
956     ["Gloves" => CFClient::Pickup::PU_GLOVES],
957     ["Cloaks" => CFClient::Pickup::PU_CLOAK],
958     ],
959    
960     ["Readables", 2, 2,
961     ["Spellbooks" => CFClient::Pickup::PU_SPELLBOOK],
962     ["Skillscrolls" => CFClient::Pickup::PU_SKILLSCROLL],
963     ["Normal Books/Scrolls" => CFClient::Pickup::PU_READABLES],
964     ],
965     ["Misc", 2, 7,
966     ["Food" => CFClient::Pickup::PU_FOOD],
967     ["Drinks" => CFClient::Pickup::PU_DRINK],
968     ["Valuables (Money, Gems)" => CFClient::Pickup::PU_VALUABLES],
969     ["Keys" => CFClient::Pickup::PU_KEY],
970     ["Magical Items" => CFClient::Pickup::PU_MAGICAL],
971     ["Potions" => CFClient::Pickup::PU_POTION],
972     ["Magic Devices" => CFClient::Pickup::PU_MAGIC_DEVICE],
973     ["Ignore cursed" => CFClient::Pickup::PU_NOT_CURSED],
974     ["Jewelery" => CFClient::Pickup::PU_JEWELS],
975     ],
976 elmex 1.43 )
977     {
978 root 1.51 my ($title, $x, $y, @bits) = @$_;
979     $table->add ($x, $y, new CFClient::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
980    
981     for (@bits) {
982     ++$y;
983    
984 elmex 1.43 my $mask = $_->[1];
985 root 1.51 $table->add ($x , $y, new CFClient::UI::Label text => $_->[0], align => 1, expand => 1);
986     $table->add ($x+1, $y, new CFClient::UI::CheckBox
987 elmex 1.43 state => $CFG->{pickup} & $mask,
988     on_changed => sub {
989     my ($box, $value) = @_;
990     if ($value) {
991 elmex 1.45 $::CFG->{pickup} |= $mask;
992 elmex 1.43 } else {
993 elmex 1.45 $::CFG->{pickup} = $::CFG->{pickup} & ~$mask;
994 elmex 1.43 }
995 elmex 1.45 $::CONN->send (sprintf "command pickup %u", $::CFG->{pickup})
996     if defined $::CONN;
997 elmex 1.43 });
998     }
999     }
1000    
1001 root 1.51 $table
1002 elmex 1.43 }
1003    
1004 root 1.1 sub make_inventory_window {
1005 root 1.23 my $invwin = $INV_WINDOW = new CFClient::UI::FancyFrame
1006 root 1.32 x => "center",
1007     y => "center",
1008     force_w => $WIDTH * 9/10,
1009     force_h => $HEIGHT * 9/10,
1010     title => "Inventory",
1011 root 1.21 ;
1012 root 1.1
1013 root 1.21 $invwin->add (my $hb = new CFClient::UI::HBox homogeneous => 1);
1014 root 1.1
1015 root 1.21 $hb->add (my $vb1 = new CFClient::UI::VBox);
1016     $vb1->add (new CFClient::UI::Label align => 0, text => "Player");
1017     $vb1->add ($INV = new CFClient::UI::Inventory expand => 1);
1018 root 1.1
1019 root 1.21 $hb->add (my $vb2 = new CFClient::UI::VBox);
1020 elmex 1.17
1021 elmex 1.27 $vb2->add ($INV_RIGHT_HB = new CFClient::UI::HBox);
1022 elmex 1.14
1023 root 1.1 $vb2->add ($INVR = new CFClient::UI::Inventory expand => 1);
1024    
1025 elmex 1.27 # XXX: Call after $INVR = ... because set_opencont sets the items
1026     CFClient::Protocol::set_opencont ($::CONN, 0, "Floor");
1027    
1028 root 1.1 $invwin
1029     }
1030    
1031 root 1.49 sub spell_setup {
1032     new CFClient::UI::SpellList
1033 elmex 1.38 }
1034    
1035 root 1.49 sub keyboard_setup {
1036 elmex 1.24 my $binding_list = new CFClient::UI::VBox;
1037    
1038 elmex 1.34 my $refresh;
1039     $refresh = sub {
1040 elmex 1.24 $binding_list->clear ();
1041    
1042     for my $mod (keys %{$::CFG->{bindings}}) {
1043     for my $sym (keys %{$::CFG->{bindings}->{$mod}}) {
1044     my $cmds = $::CFG->{bindings}->{$mod}->{$sym};
1045     next unless ref $cmds eq 'ARRAY' and @$cmds > 0;
1046    
1047     my $lbl = join "; ", @$cmds;
1048 elmex 1.34 my $nam = CFClient::Binder::keycombo_to_name ($mod, $sym);
1049 elmex 1.24 $binding_list->add (my $hb = new CFClient::UI::HBox);
1050     $hb->add (new CFClient::UI::Button
1051 elmex 1.25 text => "delete",
1052 elmex 1.34 tooltip => "Deletes the binding",
1053 elmex 1.24 on_activate => sub {
1054     $binding_list->remove ($hb);
1055     delete $::CFG->{bindings}->{$mod}->{$sym};
1056     });
1057 elmex 1.34
1058     $hb->add (new CFClient::UI::Button
1059     text => "edit",
1060     tooltip => "Edits the binding",
1061     on_activate => sub {
1062     $::BIND_EDITOR->set_binding (
1063     $mod, $sym, $::CFG->{bindings}->{$mod}->{$sym},
1064     sub {
1065     my ($nmod, $nsym, $ncmds) = @_;
1066     delete $::CFG->{bindings}->{$mod}->{$sym};
1067     $::CFG->{bindings}->{$nmod}->{$nsym} = $ncmds;
1068     $refresh->();
1069 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1070     $SETUP_DIALOG->show;
1071 elmex 1.34 },
1072     sub {
1073 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1074     $SETUP_DIALOG->show;
1075 elmex 1.34 });
1076     $::BIND_EDITOR->show;
1077 root 1.49 $SETUP_DIALOG->hide;
1078 elmex 1.34 });
1079    
1080     $hb->add (new CFClient::UI::Label text => "(Key: $nam)");
1081 elmex 1.24 $hb->add (new CFClient::UI::Label text => $lbl, expand => 1);
1082     }
1083     }
1084     };
1085    
1086 root 1.49 my $vb = new CFClient::UI::VBox;
1087 elmex 1.35 $vb->add ($binding_list);
1088     $vb->add (my $hb = new CFClient::UI::HBox);
1089 root 1.49
1090 elmex 1.35 $hb->add (new CFClient::UI::Button
1091 elmex 1.34 text => "record new",
1092 elmex 1.35 expand => 1,
1093 elmex 1.34 tooltip => "This button opens the binding editor with an empty binding.",
1094     on_activate => sub {
1095     $::BIND_EDITOR->set_binding (undef, undef, [],
1096     sub {
1097     my ($mod, $sym, $cmds) = @_;
1098     $::CFG->{bindings}->{$mod}->{$sym} = $cmds;
1099     $refresh->();
1100 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1101     $SETUP_DIALOG->show;
1102 elmex 1.34 },
1103     sub {
1104 root 1.49 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1105     $SETUP_DIALOG->show;
1106 root 1.53 },
1107     );
1108 root 1.49 $SETUP_DIALOG->hide;
1109 elmex 1.34 $::BIND_EDITOR->show;
1110     },
1111     );
1112 root 1.49
1113 elmex 1.35 $hb->add (new CFClient::UI::Button
1114     text => "close",
1115     tooltip => "Closes the binding window",
1116     expand => 1,
1117     on_activate => sub {
1118 root 1.49 $SETUP_DIALOG->hide;
1119 elmex 1.35 }
1120     );
1121    
1122 elmex 1.24 $refresh->();
1123 root 1.49
1124     $vb
1125 elmex 1.24 }
1126    
1127 root 1.1 sub make_help_window {
1128     my $win = new CFClient::UI::FancyFrame
1129 root 1.41 x => 'center',
1130     y => 'center',
1131 root 1.55 z => 2,
1132 root 1.41 name => 'doc_browser',
1133     force_w => int $WIDTH * 7/8,
1134     force_h => int $HEIGHT * 7/8,
1135     title => "Documentation";
1136 root 1.1
1137     $win->add (my $vbox = new CFClient::UI::VBox);
1138    
1139     $vbox->add (my $buttons = new CFClient::UI::HBox);
1140     $vbox->add (my $viewer = new CFClient::UI::TextView expand => 1, fontsize => 0.8);
1141    
1142     for (
1143     [intro => "Introduction"],
1144     [manual => "Manual"],
1145     [command_help => "Commands"],
1146     [skill_help => "Skills"],
1147     ) {
1148     my ($pod, $label) = @$_;
1149    
1150     $buttons->add (new CFClient::UI::Button
1151     text => $label,
1152 root 1.18 on_activate => sub {
1153 root 1.57 my $pom = CFClient::load_pod CFClient::find_rcfile "pod/$pod.pod",
1154     doc_viewer => 1, sub { CFClient::pod_to_pango_list $_[0] };
1155 root 1.1
1156     $viewer->clear;
1157    
1158     $viewer->add_paragraph ([1, 1, 1, 1], $_->[1], $_->[0])
1159 root 1.57 for @$pom;
1160 root 1.1
1161     $viewer->set_offset (0);
1162     },
1163     );
1164     }
1165    
1166     $viewer->add_paragraph ([1, 1, 0, 1], "<big>Use one of the buttons above to display a document.</big>");
1167    
1168     $win
1169     }
1170    
1171     sub sdl_init {
1172     CFClient::SDL_Init
1173     and die "SDL::Init failed!\n";
1174     }
1175    
1176     sub video_init {
1177     sdl_init;
1178    
1179     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1180    
1181     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1182    
1183     ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1184     $FULLSCREEN = $CFG->{fullscreen};
1185     $FAST = $CFG->{fast};
1186    
1187     CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
1188     or die "SDL_SetVideoMode failed: " . (CFClient::SDL_GetError) . "\n";
1189    
1190     $SDL_ACTIVE = 1;
1191     $LAST_REFRESH = time - 0.01;
1192    
1193 root 1.10 CFClient::OpenGL::init;
1194 root 1.1
1195     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1196    
1197     $CFClient::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1198    
1199     #############################################################################
1200    
1201     if ($DEBUG_STATUS) {
1202     CFClient::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1203     } else {
1204     # create the widgets
1205    
1206 root 1.30 $DEBUG_STATUS = new CFClient::UI::Label
1207     padding => 0,
1208     z => 100,
1209     force_x => "max",
1210     force_y => 0;
1211 root 1.1 $DEBUG_STATUS->show;
1212 elmex 1.34
1213     $BIND_EDITOR = new CFClient::UI::BindEditor (x => "max", y => 0);
1214    
1215 root 1.1 $STATUSBOX = new CFClient::UI::Statusbox;
1216 root 1.54 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1217 root 1.1
1218     (new CFClient::UI::Frame
1219     bg => [0, 0, 0, 0.4],
1220 root 1.30 force_x => 0,
1221     force_y => "max",
1222 root 1.1 child => $STATUSBOX,
1223     )->show;
1224    
1225     CFClient::UI::FancyFrame->new (
1226 root 1.47 title => "Map",
1227 root 1.42 name => "mapmap",
1228 root 1.30 x => 0,
1229     y => $FONTSIZE + 8,
1230 root 1.1 border_bg => [1, 1, 1, 192/255],
1231     bg => [1, 1, 1, 0],
1232     child => ($MAPMAP = new CFClient::MapWidget::MapMap
1233     tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
1234     ),
1235     )->show;
1236    
1237     $MAPWIDGET = new CFClient::MapWidget;
1238     $MAPWIDGET->connect (activate_console => sub {
1239     my ($mapwidget, $preset) = @_;
1240    
1241     if ($CONSOLE) {
1242     $CONSOLE->{input}->{auto_activated} = 1;
1243     $CONSOLE->{input}->focus_in;
1244    
1245     if ($preset && $CONSOLE->{input}->get_text eq '') {
1246     $CONSOLE->{input}->set_text ($preset);
1247     }
1248     }
1249     });
1250     $MAPWIDGET->show;
1251     $MAPWIDGET->focus_in;
1252    
1253     $LOGVIEW = new CFClient::UI::TextView
1254     expand => 1,
1255     font => $FONT_FIXED,
1256     fontsize => $::CFG->{log_fontsize},
1257     can_hover => 1,
1258     can_events => 1,
1259     tooltip => "<b>Server Log</b>. This text viewer contains all the messages sent by the server.",
1260     ;
1261    
1262 root 1.49 $SETUP_DIALOG = new CFClient::UI::FancyFrame
1263     title => "Setup",
1264     name => "setup_dialog",
1265     x => 'center',
1266     y => 'center',
1267 root 1.53 z => 2,
1268 root 1.49 force_w => $::WIDTH * 0.6,
1269     force_h => $::HEIGHT * 0.6,
1270     ;
1271    
1272 root 1.50 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFClient::UI::Notebook expand => 1, debug => 1,
1273     filter => new CFClient::UI::ScrolledWindow xxx => 1, expand => 1, scroll_y => 1);
1274 root 1.49
1275     $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup,
1276     "Configure the server to play on, your username, password and other server-related options.");
1277     $SETUP_NOTEBOOK->add (Pickup => autopickup_setup,
1278     "Configure autopicking stetings, i.e. which items you will pick up automatically when walking over them.");
1279     $SETUP_NOTEBOOK->add (Graphics => graphics_setup,
1280     "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1281     $SETUP_NOTEBOOK->add (Audio => audio_setup,
1282     "Configure the use of audio, sound effects and background music.");
1283     $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1284     "Lets you define, edit and delete bindings."
1285 root 1.54 . "There is a shortcut for making bindings: <b>Left Control + Insert</b> opens the binding editor "
1286 root 1.49 . "with nothing set and the recording started. After doing the actions you "
1287 root 1.54 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
1288 root 1.49 . "After pressing the combo the binding will be saved automatically and the "
1289     . "binding editor closes");
1290     $SETUP_NOTEBOOK->add (Spells => $SETUP_SPELLS = spell_setup,
1291     "Displays all spells you have and lets you edit keyboard shortcuts for them.");
1292    
1293 root 1.57 $BUTTONBAR = new CFClient::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1294 root 1.1
1295 root 1.49 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1296     tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1297    
1298 root 1.1 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window,
1299     tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1300    
1301     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
1302    
1303     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Stats Window", other => make_stats_window,
1304     tooltip => "Toggles the statistics window, where all your Stats and Resistances are being displayed at all times.");
1305     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Inventory", other => make_inventory_window,
1306 root 1.51 tooltip => "Toggles the inventory window, where you can manage your loot (or treasures :). "
1307 root 1.52 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory.");
1308 root 1.1
1309     $BUTTONBAR->add (new CFClient::UI::Button
1310     text => "Save Config",
1311     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
1312 root 1.18 on_activate => sub {
1313 elmex 1.16 $::CFG->{layout} = CFClient::UI::get_layout;
1314 root 1.28 CFClient::write_cfg "$Crossfire::VARDIR/cfplusrc";
1315 root 1.1 status "Configuration Saved";
1316     },
1317     );
1318    
1319     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Help!", other => make_help_window,
1320     tooltip => "View Documentation");
1321    
1322     $BUTTONBAR->add (new CFClient::UI::Button
1323 root 1.18 text => "Quit",
1324     tooltip => "Terminates the program",
1325     on_activate => sub {
1326 root 1.1 if ($CONN) {
1327     open_quit_dialog;
1328     } else {
1329     exit;
1330     }
1331     },
1332     );
1333    
1334     $BUTTONBAR->show;
1335 root 1.49 $SETUP_DIALOG->show;
1336     }
1337 root 1.1
1338 root 1.49 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1339 root 1.1 }
1340    
1341     sub video_shutdown {
1342     undef $SDL_ACTIVE;
1343     }
1344    
1345     my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
1346     my $bgmusic;#TODO#hack#d#
1347    
1348     sub audio_channel_finished {
1349     my ($channel) = @_;
1350    
1351     #warn "channel $channel finished\n";#d#
1352     }
1353    
1354     sub audio_music_finished {
1355     return unless $CFG->{bgm_enable};
1356    
1357     # TODO: hack, do play loop and mood music
1358     $bgmusic = new_from_file CFClient::MixMusic CFClient::find_rcfile "music/$bgmusic[0]";
1359     $bgmusic->play (0);
1360    
1361     push @bgmusic, shift @bgmusic;
1362     }
1363    
1364     sub audio_init {
1365     if ($CFG->{audio_enable}) {
1366     if (open my $fh, "<", CFClient::find_rcfile "sounds/config") {
1367     $SDL_MIXER = !CFClient::Mix_OpenAudio;
1368    
1369     unless ($SDL_MIXER) {
1370     status "Unable to open sound device: there will be no sound";
1371     return;
1372     }
1373    
1374     CFClient::Mix_AllocateChannels 8;
1375     CFClient::MixMusic::volume $CFG->{bgm_volume} * 128;
1376    
1377     audio_music_finished;
1378    
1379     while (<$fh>) {
1380     next if /^\s*#/;
1381     next if /^\s*$/;
1382    
1383     my ($file, $volume, $event) = split /\s+/, $_, 3;
1384    
1385     push @SOUNDS, "$volume,$file";
1386    
1387     $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1388     my $chunk = new_from_file CFClient::MixChunk CFClient::find_rcfile "sounds/$file";
1389     $chunk->volume ($volume * 128 / 100);
1390     $chunk
1391     };
1392     }
1393     } else {
1394     status "unable to open sound config: $!";
1395     }
1396     }
1397     }
1398    
1399     sub audio_shutdown {
1400     CFClient::Mix_CloseAudio if $SDL_MIXER;
1401     undef $SDL_MIXER;
1402     @SOUNDS = ();
1403     %AUDIO_CHUNKS = ();
1404     }
1405    
1406     my %animate_object;
1407     my $animate_timer;
1408    
1409     my $fps = 9;
1410    
1411     my %demo;#d#
1412    
1413     sub force_refresh {
1414     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1415 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1416 root 1.1
1417     $CFClient::UI::ROOT->draw;
1418    
1419     $WANT_REFRESH = 0;
1420     $CAN_REFRESH = 0;
1421     $LAST_REFRESH = $NOW;
1422    
1423     0 && do {
1424     # some weird model-drawing code, just a joke right now
1425     use CFClient::OpenGL;
1426    
1427     $demo{t}{eye_auv} ||= new_from_file CFClient::Texture "eye2.png" or die;
1428     $demo{t}{body_auv} ||= new_from_file CFClient::Texture "body_auv3.png" or die;
1429     $demo{r} ||= do {
1430     my $mod = Compress::LZF::sthaw do { local $/; open my $fh, "<:raw:perlio", "dread.lz3"; <$fh> };
1431     $mod->{v} = pack "f*", @{$mod->{v}};
1432     $_ = [scalar @$_, pack "S!*", @$_]
1433     for values %{$mod->{g}};
1434     $mod
1435     };
1436    
1437     my $r = $demo{r} or die;
1438    
1439     glDepthMask 1;
1440     glClear GL_DEPTH_BUFFER_BIT;
1441     glEnable GL_TEXTURE_2D;
1442     glEnable GL_DEPTH_TEST;
1443     glEnable GL_CULL_FACE;
1444     glShadeModel $::FAST ? GL_FLAT : GL_SMOOTH;
1445    
1446     glMatrixMode GL_PROJECTION;
1447     glLoadIdentity;
1448     glFrustum -1 * ($::WIDTH / $::HEIGHT), 1 * ($::WIDTH / $::HEIGHT), 1, -1, 1, 10000;
1449     #glOrtho 0, $::WIDTH, 0, $::HEIGHT, -10000, 10000;
1450     glMatrixMode GL_MODELVIEW;
1451     glLoadIdentity;
1452    
1453     glPushMatrix;
1454     glTranslate 0, 0, -800;
1455     glScale 1, -1, 1;
1456     glRotate $NOW * 1000 % 36000 / 5, 0, 1, 0;
1457     glRotate $NOW * 1000 % 36000 / 6, 1, 0, 0;
1458     glRotate $NOW * 1000 % 36000 / 7, 0, 0, 1;
1459     glScale 50, 50, 50;
1460    
1461     glInterleavedArrays GL_T2F_N3F_V3F, 0, $r->{v};
1462     while (my ($k, $v) = each %{$r->{g}}) {
1463     glBindTexture GL_TEXTURE_2D, ($demo{t}{$k}{name} or die);
1464     glDrawElements GL_TRIANGLES, $v->[0], GL_UNSIGNED_SHORT, $v->[1];
1465     }
1466    
1467     glPopMatrix;
1468    
1469     glShadeModel GL_FLAT;
1470     glDisable GL_DEPTH_TEST;
1471     glDisable GL_TEXTURE_2D;
1472     glDepthMask 0;
1473    
1474     $WANT_REFRESH++;
1475     };
1476    
1477     CFClient::SDL_GL_SwapBuffers;
1478     }
1479    
1480 root 1.49 my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1481 root 1.1 $NOW = time;
1482    
1483     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1484     for CFClient::SDL_PollEvent;
1485    
1486     if (%animate_object) {
1487     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1488     $WANT_REFRESH++;
1489     }
1490    
1491     if ($WANT_REFRESH) {
1492     force_refresh;
1493     } else {
1494     $CAN_REFRESH = 1;
1495     }
1496     });
1497    
1498     sub animation_start {
1499     my ($widget) = @_;
1500     $animate_object{$widget} = $widget;
1501     }
1502    
1503     sub animation_stop {
1504     my ($widget) = @_;
1505     delete $animate_object{$widget};
1506     }
1507    
1508     # check once/second for faces that need to be prefetched
1509     # this should, of course, only run on demand, but
1510     # SDL forces worse things on us....
1511    
1512     Event->timer (after => 1, interval => 0.25, cb => sub {
1513     $CONN->face_prefetch
1514     if $CONN;
1515     });
1516    
1517     %SDL_CB = (
1518     CFClient::SDL_QUIT => sub {
1519     Event::unloop -1;
1520     },
1521     CFClient::SDL_VIDEORESIZE => sub {
1522     },
1523     CFClient::SDL_VIDEOEXPOSE => sub {
1524     CFClient::UI::full_refresh;
1525     },
1526     CFClient::SDL_ACTIVEEVENT => sub {
1527     # printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d#
1528     },
1529     CFClient::SDL_KEYDOWN => sub {
1530     if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) {
1531     # alt-enter
1532     video_shutdown;
1533     $CFG->{fullscreen} = !$CFG->{fullscreen};
1534     video_init;
1535     } else {
1536     CFClient::UI::feed_sdl_key_down_event ($_[0]);
1537     }
1538     },
1539     CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event,
1540     CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event,
1541     CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event,
1542     CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event,
1543     CFClient::SDL_USEREVENT => sub {
1544     if ($_[0]{code} == 1) {
1545     audio_channel_finished $_[0]{data1};
1546     } elsif ($_[0]{code} == 0) {
1547     audio_music_finished;
1548     }
1549     },
1550     );
1551    
1552     #############################################################################
1553    
1554     $SIG{INT} = $SIG{TERM} = sub { exit };
1555    
1556     {
1557 root 1.49 local $SIG{__DIE__} = sub {
1558     return unless defined $^S && !$^S;
1559     Carp::confess $_[1];#d#TODO: remove when stable
1560     CFClient::fatal $_[0];
1561     };
1562 root 1.1
1563 root 1.28 CFClient::read_cfg "$Crossfire::VARDIR/cfplusrc";
1564 elmex 1.16 CFClient::UI::set_layout ($::CFG->{layout});
1565 root 1.1
1566     my %DEF_CFG = (
1567     sdl_mode => 0,
1568     width => 640,
1569     height => 480,
1570     fullscreen => 0,
1571     fast => 0,
1572     map_scale => 1,
1573     fow_enable => 1,
1574     fow_intensity => 0.45,
1575     fow_smooth => 0,
1576     gui_fontsize => 1,
1577     log_fontsize => 1,
1578     gauge_fontsize=> 1,
1579     gauge_size => 0.35,
1580     stat_fontsize => 1,
1581     mapsize => 100,
1582     host => "crossfire.schmorp.de",
1583     say_command => 'say',
1584     audio_enable => 1,
1585     bgm_enable => 1,
1586     bgm_volume => 0.25,
1587     face_prefetch => 0,
1588     output_sync => 1,
1589     output_count => 1,
1590     );
1591    
1592     while (my ($k, $v) = each %DEF_CFG) {
1593     $CFG->{$k} = $v unless exists $CFG->{$k};
1594     }
1595    
1596     sdl_init;
1597    
1598     @SDL_MODES = reverse
1599     grep $_->[0] >= 640 && $_->[1] >= 480,
1600     CFClient::SDL_ListModes;
1601    
1602     @SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
1603    
1604     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
1605    
1606     {
1607     my @fonts = map CFClient::find_rcfile "fonts/$_", qw(
1608     DejaVuSans.ttf
1609     DejaVuSansMono.ttf
1610     DejaVuSans-Bold.ttf
1611     DejaVuSansMono-Bold.ttf
1612     DejaVuSans-Oblique.ttf
1613     DejaVuSansMono-Oblique.ttf
1614     DejaVuSans-BoldOblique.ttf
1615     DejaVuSansMono-BoldOblique.ttf
1616     );
1617    
1618     CFClient::add_font $_ for @fonts;
1619    
1620     CFClient::pango_init;
1621    
1622     $FONT_PROP = new_from_file CFClient::Font $fonts[0];
1623     $FONT_FIXED = new_from_file CFClient::Font $fonts[1];
1624    
1625     $FONT_PROP->make_default;
1626     }
1627    
1628     # compare mono (ft) vs. rgba (cairo)
1629     # ft - 1.8s, cairo 3s, even in alpha-only mode
1630     # for my $rgba (0..1) {
1631     # my $t1 = Time::HiRes::time;
1632     # for (1..1000) {
1633     # my $layout = CFClient::Layout->new ($rgba);
1634     # $layout->set_text ("hallo" x 100);
1635     # $layout->render;
1636     # }
1637     # my $t2 = Time::HiRes::time;
1638     # warn $t2-$t1;
1639     # }
1640    
1641     video_init;
1642     audio_init;
1643     }
1644    
1645     Event::loop;
1646    
1647     END { CFClient::SDL_Quit }
1648    
1649     =head1 NAME
1650    
1651 root 1.28 cfplus - A Crossfire+ and Crossfire game client
1652 root 1.1
1653     =head1 SYNOPSIS
1654    
1655     Just run it - no commandline arguments are supported.
1656    
1657     =head1 USAGE
1658    
1659 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
1660 root 1.1 fullscreen and interactively.
1661    
1662 root 1.39 =head1 DEBUGGING
1663    
1664    
1665     CFPLUS_DEBUG - environment variable
1666    
1667     1 draw borders around widgets
1668     2 add low-level widget info to tooltips
1669     4 show fps
1670     8 suppress tooltips
1671    
1672 root 1.1 =head1 AUTHOR
1673    
1674     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
1675    
1676    
1677