ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.54
Committed: Mon Jun 5 01:59:59 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.53: +3 -3 lines
Log Message:
statusbox now freezes when tooltip is visible for any entries

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