ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.59
Committed: Mon Jun 5 22:30:35 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.58: +2 -0 lines
Log Message:
partially reimplemented the spell list widget

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