ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.56
Committed: Mon Jun 5 05:23:21 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.55: +1 -3 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3     use strict;
4     use utf8;
5    
6     # do things only needed for single-binary version (par)
7     BEGIN {
8     if (%PAR::LibCache) {
9     @INC = grep ref, @INC; # weed out all paths except pars loader refs
10    
11     while (my ($filename, $zip) = each %PAR::LibCache) {
12     for ($zip->memberNames) {
13     next unless /^\/root\/(.*)/;
14     $zip->extractMember ($_, "$ENV{PAR_TEMP}/$1")
15     unless -e "$ENV{PAR_TEMP}/$1";
16     }
17     }
18    
19     # TODO: pango-rc file, anybody?
20    
21     unshift @INC, $ENV{PAR_TEMP};
22     }
23     }
24    
25     # need to do it again because that pile of garbage called PAR nukes it before main
26     unshift @INC, $ENV{PAR_TEMP}
27     if %PAR::LibCache;
28    
29     use Time::HiRes 'time';
30     use Event;
31    
32     use Crossfire;
33 root 1.12 use Crossfire::Protocol::Constants;
34 root 1.1
35     use Compress::LZF;
36    
37     use CFClient;
38 root 1.10 use CFClient::OpenGL ();
39 root 1.11 use CFClient::Protocol;
40 root 1.1 use CFClient::UI;
41     use CFClient::MapWidget;
42    
43     $Event::DIED = sub {
44     # TODO: display dialog box or so
45 root 1.49 Carp::confess $_[1];#d#TODO: remove when stable
46 root 1.1 CFClient::error $_[1];
47     };
48    
49     #$SIG{__WARN__} = sub { Carp::cluck $_[0] };#d#
50    
51     our $VERSION = '0.1';
52    
53     my $MAX_FPS = 60;
54     my $MIN_FPS = 5; # unused as of yet
55    
56     our $META_SERVER = "crossfire.real-time.com:13326";
57    
58     our $LAST_REFRESH;
59     our $NOW;
60    
61     our $CFG;
62     our $CONN;
63     our $FAST; # fast, low-quality mode, possibly useful for software-rendering
64    
65     our $WANT_REFRESH;
66     our $CAN_REFRESH;
67    
68     our @SDL_MODES;
69     our $WIDTH;
70     our $HEIGHT;
71     our $FULLSCREEN;
72     our $FONTSIZE;
73    
74     our $FONT_PROP;
75     our $FONT_FIXED;
76    
77     our $MAP;
78     our $MAPMAP;
79     our $MAPWIDGET;
80     our $BUTTONBAR;
81     our $LOGVIEW;
82     our $CONSOLE;
83     our $METASERVER;
84     our $LOGIN_BUTTON;
85     our $QUIT_DIALOG;
86 root 1.40 our $HOST_ENTRY;
87 root 1.49
88     our $SETUP_DIALOG;
89     our $SETUP_NOTEBOOK;
90     our $SETUP_SERVER;
91     our $SETUP_KEYBOARD;
92     our $SETUP_SPELLS;
93 root 1.1
94     our $FLOORBOX;
95     our $GAUGES;
96     our $STATWIDS;
97    
98     our $SDL_ACTIVE;
99     our %SDL_CB;
100    
101     our $SDL_MIXER;
102     our @SOUNDS; # event => file mapping
103     our %AUDIO_CHUNKS; # audio files
104    
105     our $ALT_ENTER_MESSAGE;
106     our $STATUSBOX;
107     our $DEBUG_STATUS;
108    
109 root 1.23 our $INV_WINDOW;
110 root 1.1 our $INV;
111     our $INVR;
112 elmex 1.27 our $INV_RIGHT_HB;
113 root 1.1
114 elmex 1.34 our $BIND_EDITOR;
115 elmex 1.24
116 elmex 1.43 our $PICKUP_CFG;
117 elmex 1.38
118 root 1.1 sub status {
119     $STATUSBOX->add (CFClient::UI::Label::escape $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
120     }
121    
122     sub debug {
123     $DEBUG_STATUS->set_text ($_[0]);
124     }
125    
126     sub start_game {
127     status "logging in...";
128    
129 root 1.23 $LOGIN_BUTTON->set_text ("Logout");
130 root 1.49 $SETUP_DIALOG->hide;
131 root 1.23
132 root 1.1 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
133    
134 root 1.11 my ($host, $port) = split /:/, $CFG->{host};
135    
136 root 1.1 $MAP = new CFClient::Map $mapsize, $mapsize;
137    
138     $CONN = eval {
139 root 1.11 new CFClient::Protocol
140 root 1.1 host => $host,
141     port => $port || 13327,
142     user => $CFG->{user},
143     pass => $CFG->{password},
144     mapw => $mapsize,
145     maph => $mapsize,
146 root 1.11
147     map_widget => $MAPWIDGET,
148     logview => $LOGVIEW,
149     statusbox => $STATUSBOX,
150     map => $MAP,
151     mapmap => $MAPMAP,
152    
153     sound_play => sub {
154     my ($x, $y, $soundnum, $type) = @_;
155    
156     $SDL_MIXER
157     or return;
158    
159     my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
160     or return;
161    
162     $chunk->play;
163     },
164 root 1.1 };
165    
166     if ($CONN) {
167     CFClient::lowdelay fileno $CONN->{fh};
168    
169     status "login successful";
170     } else {
171     status "unable to connect";
172     stop_game();
173     }
174     }
175    
176     sub stop_game {
177 root 1.23 $LOGIN_BUTTON->set_text ("Login");
178 root 1.53 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER);
179 root 1.49 $SETUP_DIALOG->show;
180 root 1.23 $INV_WINDOW->hide;
181    
182 root 1.1 return unless $CONN;
183    
184     status "connection closed";
185 root 1.23
186 root 1.1 $CONN->destroy;
187     $CONN = 0; # false, does not autovivify
188     }
189    
190 root 1.49 sub graphics_setup {
191     my $vbox = new CFClient::UI::VBox;
192 root 1.30
193 root 1.1 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
194    
195     $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode");
196     $table->add (1, 0, my $hbox = new CFClient::UI::HBox);
197    
198 root 1.31 $hbox->add (my $mode_slider = new CFClient::UI::Slider force_w => $WIDTH * 0.1, expand => 1, range => [$CFG->{sdl_mode}, 0, $#SDL_MODES, 0, 1]);
199 root 1.1 $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999");
200    
201     $mode_slider->connect (changed => sub {
202     my ($self, $value) = @_;
203    
204     $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
205     $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]});
206     });
207     $mode_slider->emit (changed => $mode_slider->{range}[0]);
208    
209     my $row = 1;
210    
211     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fullscreen");
212     $table->add (1, $row++, new CFClient::UI::CheckBox
213     state => $CFG->{fullscreen},
214     tooltip => "Bring the client into fullscreen mode.",
215 root 1.18 on_changed => sub {
216 root 1.1 my ($self, $value) = @_;
217     $CFG->{fullscreen} = $value;
218     }
219     );
220    
221     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
222     $table->add (1, $row++, new CFClient::UI::CheckBox
223     state => $CFG->{fast},
224     tooltip => "Lower the visual quality considerably to speed up rendering.",
225 root 1.18 on_changed => sub {
226 root 1.1 my ($self, $value) = @_;
227     $CFG->{fast} = $value;
228     }
229     );
230    
231     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Map Scale");
232     $table->add (1, $row++, new CFClient::UI::Slider
233     range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
234     tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
235 root 1.18 on_changed => sub {
236 root 1.1 my ($self, $value) = @_;
237     $CFG->{map_scale} = 2 ** $value;
238     }
239     );
240    
241     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War");
242     $table->add (1, $row++, new CFClient::UI::CheckBox
243     state => $CFG->{fow_enable},
244     tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.",
245 root 1.18 on_changed => sub {
246 root 1.1 my ($self, $value) = @_;
247     $CFG->{fow_enable} = $value;
248     }
249     );
250    
251     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Intensity");
252     $table->add (1, $row++, new CFClient::UI::Slider
253     range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256],
254     tooltip => "<b>Fog of War Lightness.</b> The higher the intensity, the lighter the Fog-of-War color. Changes are instant.",
255 root 1.18 on_changed => sub {
256 root 1.1 my ($self, $value) = @_;
257     $CFG->{fow_intensity} = $value;
258     }
259     );
260    
261     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Smooth");
262     $table->add (1, $row++, new CFClient::UI::CheckBox
263     state => $CFG->{fow_smooth},
264     tooltip => "Smooth the Fog-of-War a bit to make it more realistic. Changes are instant.",
265 root 1.18 on_changed => sub {
266 root 1.1 my ($self, $value) = @_;
267     $CFG->{fow_smooth} = $value;
268 root 1.15 status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFClient::OpenGL::GL_VERSION < 1.2;
269 root 1.1 }
270     );
271    
272     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
273     $table->add (1, $row++, new CFClient::UI::Slider
274     range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
275     tooltip => "The base font size used by most GUI elements that do not have their own setting.",
276 root 1.18 on_changed => sub { $CFG->{gui_fontsize} = $_[1] },
277 root 1.1 );
278    
279     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Message Fontsize");
280     $table->add (1, $row++, new CFClient::UI::Slider
281     range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1],
282     tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant.",
283 root 1.18 on_changed => sub { $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = $_[1]) },
284 root 1.1 );
285    
286     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Stats Fontsize");
287    
288     $table->add (1, $row++, new CFClient::UI::Slider
289     range => [$CFG->{stat_fontsize}, 0.5, 2, 0, 0.1],
290     tooltip => "The font size used by the <b>statistics window</b> only. Changes are instant.",
291 root 1.18 on_changed => sub {
292 root 1.1 $CFG->{stat_fontsize} = $_[1];
293     &set_stats_window_fontsize;
294     }
295     );
296    
297     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
298     $table->add (1, $row++, new CFClient::UI::Slider
299     range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1],
300     tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.",
301 root 1.18 on_changed => sub {
302 root 1.1 $CFG->{gauge_fontsize} = $_[1];
303     &set_gauge_window_fontsize;
304     }
305     );
306    
307     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size");
308     $table->add (1, $row++, new CFClient::UI::Slider
309 root 1.18 range => [$CFG->{gauge_size}, 0.2, 0.8],
310     tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.",
311     on_changed => sub {
312 root 1.1 $CFG->{gauge_size} = $_[1];
313     $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
314     }
315     );
316    
317     $table->add (1, $row++, new CFClient::UI::Button
318     expand => 1, align => 0, text => "Apply",
319     tooltip => "Apply the video settings",
320 root 1.18 on_activate => sub {
321 root 1.1 video_shutdown ();
322     video_init ();
323     }
324     );
325    
326 root 1.49 $vbox
327     }
328    
329     sub audio_setup {
330     my $vbox = new CFClient::UI::VBox;
331    
332     $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
333    
334     my $row = 0;
335    
336 root 1.1 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Audio Enable");
337     $table->add (1, $row++, new CFClient::UI::CheckBox
338     state => $CFG->{audio_enable},
339     tooltip => "<b>Master Audio Enable.</b> If enabled, sound effects and music will be played. If disabled, no audio will be used and the soundcard will not be opened.",
340 root 1.18 on_changed => sub {
341 root 1.1 $CFG->{audio_enable} = $_[1];
342     }
343     );
344     # $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Effects Volume");
345 root 1.18 # $table->add (1, 8, new CFClient::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
346 root 1.1 # $CFG->{effects_volume} = $_[1];
347     # });
348     $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Background Music");
349     $table->add (1, $row++, my $hbox = new CFClient::UI::HBox);
350     $hbox->add (new CFClient::UI::CheckBox
351     expand => 1, state => $CFG->{bgm_enable},
352     tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
353 root 1.18 on_changed => sub {
354 root 1.1 $CFG->{bgm_enable} = $_[1];
355     }
356     );
357     $hbox->add (new CFClient::UI::Slider
358     expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
359     tooltip => "The volume of the background music. Changes are instant.",
360 root 1.18 on_changed => sub {
361 root 1.1 $CFG->{bgm_volume} = $_[1];
362     CFClient::MixMusic::volume $_[1] * 128;
363     }
364     );
365    
366     $table->add (1, $row++, new CFClient::UI::Button
367     expand => 1, align => 0, text => "Apply",
368     tooltip => "Apply the audio settings",
369 root 1.18 on_activate => sub {
370 root 1.1 audio_shutdown ();
371     audio_init ();
372     }
373     );
374    
375 root 1.49 $vbox
376 root 1.1 }
377    
378     sub set_stats_window_fontsize {
379     for (values %{$STATWIDS}) {
380     $_->set_fontsize ($::CFG->{stat_fontsize});
381     }
382     }
383    
384     sub set_gauge_window_fontsize {
385     for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
386     $_->set_fontsize ($::CFG->{gauge_fontsize});
387     }
388     }
389    
390     sub make_gauge_window {
391     my $gh = int $HEIGHT * $CFG->{gauge_size};
392    
393     my $win = new CFClient::UI::Frame (
394 root 1.30 force_x => 0,
395     force_y => "max",
396     force_w => $WIDTH,
397     force_h => $gh,
398 root 1.1 );
399    
400     $win->add (my $hbox = new CFClient::UI::HBox
401     children => [
402     (new CFClient::UI::HBox expand => 1),
403     (new CFClient::UI::VBox children => [
404     (new CFClient::UI::Empty expand => 1),
405 root 1.2 (new CFClient::UI::Frame bg => [0, 0, 0, 0.4], child => ($FLOORBOX = new CFClient::UI::Table)),
406 root 1.1 ]),
407     (my $vbox = new CFClient::UI::VBox),
408     ],
409     );
410    
411     $vbox->add (new CFClient::UI::HBox
412     expand => 1,
413     children => [
414     (new CFClient::UI::Empty expand => 1),
415     (my $hb = new CFClient::UI::HBox),
416     ],
417     );
418    
419     $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp',
420     tooltip => "<b>Health points</b>. Measures of how much damage you can take before dying. Hit points are determined from your level and are influenced by the value of your Con. Hp value may range between 1 to beyond 500 and higher values indicate a greater ability to withstand punishment.");
421     $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana',
422     tooltip => "<b>Spell points</b>. Measures of how much \"fuel\" you have for casting spells and incantations. Mana is calculated from your level and your Pow. Mana values can range between 1 to beyond 500 (glowing crystals can increase the current spell points beyond your normal maximum). Higher values indicate greater amounts of mana.");
423     $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace',
424     tooltip => "<b>Grace points</b> - how favored you are by your god. In game terms, how much divine magic you can cast. Your level, Wis and Pow effect what the value of grace is. Prayong on an altar of your god can increase this value beyond your normal maximum. Grace can take on large positive and negative values. Positive values indicate favor by the gods.");
425     $hb->add (my $fg = new CFClient::UI::Gauge type => 'food',
426     tooltip => "<b>Food</b>. Ranges between 0 (starving) and 999 (satiated). At a value of 0 the character begins to die. Some magic can speed up or slow down the character digestion. Healing wounds will speed up digestion too.");
427    
428     $vbox->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
429     tooltip => "<b>Experience points and overall level</b> - experience is increased as a reward for appropriate action (such as killing monsters) and may decrease as a result of a magical attack or dying. Level is directly derived from the experience value. As the level of the character increases, the character becomes able to succeed at more difficult tasks. A character's level starts at a value of 0 and may range up beyond 100.");
430     $vbox->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
431     tooltip => "<b>Ranged attack</b> - how you attack when you press shift-cursor (spell, skill, weapon etc.)");
432    
433     $GAUGES = {
434     exp => $exp, win => $win, range => $rng,
435     food => $fg, mana => $mg, hp => $hg, grace => $gg
436     };
437    
438     &set_gauge_window_fontsize;
439    
440     $win
441     }
442    
443 elmex 1.24
444 root 1.1 sub make_stats_window {
445 elmex 1.19 my $tgw = new CFClient::UI::FancyFrame
446 root 1.30 y => $HEIGHT * (2/8),
447     x => "max",
448 elmex 1.19 title => "Stats",
449 root 1.30 name => "stats_window";
450 root 1.1
451     $tgw->add (new CFClient::UI::Window child => my $vb = new CFClient::UI::VBox);
452     $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1,
453     can_hover => 1, can_events => 1,
454     tooltip => "Your name and title. You can change your title by using the <b>title</b> command, if supported by the server.");
455     $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1,
456     can_hover => 1, can_events => 1,
457     tooltip => "The map you are currently on (if supported by the server).");
458    
459 elmex 1.5 $vb->add (my $hb0 = new CFClient::UI::HBox);
460     $hb0->add ($STATWIDS->{weight} = new CFClient::UI::Label valign => 0, align => -1, text => "Weight:", expand => 1,
461     can_hover => 1, can_events => 1,
462 root 1.15 tooltip => "The weight of the player including all inventory items.");
463 elmex 1.5 $hb0->add ($STATWIDS->{m_weight} = new CFClient::UI::Label valign => 0, align => -1, text => "Max weight:", expand => 1,
464     can_hover => 1, can_events => 1,
465 root 1.15 tooltip => "The weight limit: you cannot carry more than this.");
466 elmex 1.5
467    
468 root 1.1 $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
469     $hb->add (my $tbl = new CFClient::UI::Table expand => 1);
470    
471     my $color2 = [1, 1, 0];
472    
473     for (
474     [0, 0, st_str => "Str", 30, "<b>Physical Strength</b>, determines damage dealt with weapons, how much you can carry, and how often you can attack"],
475     [0, 1, st_dex => "Dex", 30, "<b>Dexterity</b>, your physical agility. Determines chance of being hit and affects armor class and speed"],
476     [0, 2, st_con => "Con", 30, "<b>Constitution</b>, physical health and toughness. Determines how many healthpoints you can have"],
477     [0, 3, st_int => "Int", 30, "<b>Intelligence</b>, your ability to learn and use skills and incantations (both prayers and magic) and determines how much spell points you can have"],
478     [0, 4, st_wis => "Wis", 30, "<b>Wisdom</b>, the ability to learn and use divine magic (prayers). Determines how many grace points you can have"],
479     [0, 5, st_pow => "Pow", 30, "<b>Power</b>, your magical potential. Influences the strength of spell effects, and also how much your spell and grace points increase when leveling up"],
480     [0, 6, st_cha => "Cha", 30, "<b>Charisma</b>, how well you are received by NPCs. Affects buying and selling prices in shops."],
481    
482     [2, 0, st_wc => "Wc", -120, "<b>Weapon Class</b>, effectiveness of melee/missile attacks. Lower is more potent. Current weapon, level and Str are some things which effect the value of Wc. The value of Wc may range between 25 and -72."],
483     [2, 1, st_ac => "Ac", -120, "<b>Armour Class</b>, how protected you are from being hit by any attack. Lower values are better. Ac is based on your race and is modified by the Dex and current armour worn. For characters that cannot wear armour, Ac improves as their level increases."],
484     [2, 2, st_dam => "Dam", 120, "<b>Damage</b>, how much damage your melee/missile attack inflicts. Higher values indicate a greater amount of damage will be inflicted with each attack."],
485     [2, 3, st_arm => "Arm", 120, "<b>Armour</b>, how much damage (from physical attacks) will be subtracted from successful hits made upon you. This value ranges between 0 to 99%. Current armour worn primarily determines Arm value."],
486     [2, 4, st_spd => "Spd", 10.54, "<b>Speed</b>, how fast you can move. The value of speed may range between nearly 0 (\"very slow\") to higher than 5 (\"lightning fast\"). Base speed is determined from the Dex and modified downward proportionally by the amount of weight carried which exceeds the Max Carry limit. The armour worn also sets the upper limit on speed."],
487     [2, 5, st_wspd => "WSp", 10.54, "<b>Weapon Speed</b>, how many attacks you may make per unit of time (0.120s). Higher values indicate faster attack speed. Current weapon and Dex effect the value of weapon speed."],
488     ) {
489     my ($col, $row, $id, $label, $template, $tooltip) = @$_;
490    
491     $tbl->add ($col , $row, $STATWIDS->{$id} = new CFClient::UI::Label
492     font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0, align => +1, template => $template, tooltip => $tooltip);
493     $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFClient::UI::Label
494     font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0, align => -1, text => $label, tooltip => $tooltip);
495     }
496    
497     $hb->add (my $tbl2 = new CFClient::UI::Table expand => 1);
498    
499     my $row = 0;
500     my $col = 0;
501    
502     my %resist_names = (
503     slow => "<b>Slow</b> (slows you down when you are hit by the spell. Monsters will have an opportunity to come near you faster and hit you more often.)",
504     holyw => "<b>Holy Word</b> (resistance you against getting the fear when someone whose god doesn't like you spells the holy word on you.)",
505     conf => "<b>Confusion</b> (If you are hit by confusion you will move into random directions, and likely into monsters.)",
506     fire => "<b>Fire</b> (just your resistance to fire spells like burning hands, dragonbreath, meteor swarm fire, ...)",
507     depl => "<b>Depletion</b> (some monsters and other effects can cause stats depletion)",
508     magic => "<b>Magic</b> (resistance to magic spells like magic missile or similar)",
509     drain => "<b>Draining</b> (some monsters (e.g. vampires) and other effects can steal experience)",
510     acid => "<b>Acid</b> (resistance to acid, acid hurts pretty much and also corrodes your weapons)",
511     pois => "<b>Poison</b> (resistance to getting poisoned)",
512     para => "<b>Paralysation</b> (this resistance affects the chance you get paralysed)",
513     deat => "<b>Death</b> (resistance against death spells)",
514     phys => "<b>Physical</b> (this is the resistance against physical attacks, like when a monster hit you in melee combat)",
515     blind => "<b>Blind</b> (blind resistance affects the chance of a successful blinding attack)",
516     fear => "<b>Fear</b> (this attack will drive you away from monsters who cast this and hit you successfully, being resistant to this helps a lot when fighting those monsters)",
517     tund => "<b>Turn undead</b> (affects your resistancy to various forms of 'turn undead' spells. Only relevant when you are, in fact, undead...",
518     elec => "<b>Electricity</b> (resistance against electricity, spells like large lightning, small lightning, ...)",
519     cold => "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)",
520     ghit => "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)",
521     );
522     for (qw/slow holyw conf fire depl magic
523     drain acid pois para deat phys
524     blind fear tund elec cold ghit/)
525     {
526     $tbl2->add ($col, $row,
527     $STATWIDS->{"res_$_"} =
528     new CFClient::UI::Label
529     font => $FONT_FIXED,
530     template => "-100%",
531     align => +1,
532     valign => 0,
533     can_events => 1,
534     can_hover => 1,
535     tooltip => $resist_names{$_},
536     );
537     $tbl2->add ($col + 1, $row, new CFClient::UI::Image
538     font => $FONT_FIXED,
539     can_hover => 1,
540     can_events => 1,
541     image => "ui/resist/resist_$_.png",
542     tooltip => $resist_names{$_},
543     );
544    
545     $row++;
546     if ($row % 6 == 0) {
547     $col += 2;
548     $row = 0;
549     }
550     }
551    
552     &set_stats_window_fontsize;
553     update_stats_window ({});
554    
555     $tgw
556     }
557    
558 root 1.48 sub formsep($) {
559     scalar reverse join ",", unpack "(A3)*", reverse $_[0] * 1
560 root 1.1 }
561    
562     sub update_stats_window {
563     my ($stats) = @_;
564    
565 root 1.12 # I love text protocols...
566    
567     my $hp = $stats->{+CS_STAT_HP} * 1;
568     my $hp_m = $stats->{+CS_STAT_MAXHP} * 1;
569     my $sp = $stats->{+CS_STAT_SP} * 1;
570     my $sp_m = $stats->{+CS_STAT_MAXSP} * 1;
571     my $fo = $stats->{+CS_STAT_FOOD} * 1;
572 root 1.1 my $fo_m = 999;
573 root 1.12 my $gr = $stats->{+CS_STAT_GRACE} * 1;
574     my $gr_m = $stats->{+CS_STAT_MAXGRACE} * 1;
575 root 1.1
576     $GAUGES->{hp} ->set_value ($hp, $hp_m);
577     $GAUGES->{mana} ->set_value ($sp, $sp_m);
578     $GAUGES->{food} ->set_value ($fo, $fo_m);
579     $GAUGES->{grace} ->set_value ($gr, $gr_m);
580 root 1.12 $GAUGES->{exp} ->set_text ("Exp: " . (formsep $stats->{+CS_STAT_EXP64})
581     . " (lvl " . ($stats->{+CS_STAT_LEVEL} * 1) . ")");
582     my $rng = $stats->{+CS_STAT_RANGE};
583 root 1.1 $rng =~ s/^Range: //; # thank you so much dear server
584     $GAUGES->{range} ->set_text ("Rng: " . $rng);
585 root 1.12 my $title = $stats->{+CS_STAT_TITLE};
586 root 1.1 $title =~ s/^Player: //;
587     $STATWIDS->{title} ->set_text ("Title: " . $title);
588    
589 root 1.12 $STATWIDS->{st_str} ->set_text (sprintf "%d" , $stats->{+CS_STAT_STR});
590     $STATWIDS->{st_dex} ->set_text (sprintf "%d" , $stats->{+CS_STAT_DEX});
591     $STATWIDS->{st_con} ->set_text (sprintf "%d" , $stats->{+CS_STAT_CON});
592     $STATWIDS->{st_int} ->set_text (sprintf "%d" , $stats->{+CS_STAT_INT});
593     $STATWIDS->{st_wis} ->set_text (sprintf "%d" , $stats->{+CS_STAT_WIS});
594     $STATWIDS->{st_pow} ->set_text (sprintf "%d" , $stats->{+CS_STAT_POW});
595     $STATWIDS->{st_cha} ->set_text (sprintf "%d" , $stats->{+CS_STAT_CHA});
596     $STATWIDS->{st_wc} ->set_text (sprintf "%d" , $stats->{+CS_STAT_WC});
597     $STATWIDS->{st_ac} ->set_text (sprintf "%d" , $stats->{+CS_STAT_AC});
598     $STATWIDS->{st_dam} ->set_text (sprintf "%d" , $stats->{+CS_STAT_DAM});
599     $STATWIDS->{st_arm} ->set_text (sprintf "%d" , $stats->{+CS_STAT_ARMOUR});
600     $STATWIDS->{st_spd} ->set_text (sprintf "%.1f", $stats->{+CS_STAT_SPEED});
601     $STATWIDS->{st_wspd}->set_text (sprintf "%.1f", $stats->{+CS_STAT_WEAP_SP});
602 root 1.1
603 root 1.12 $STATWIDS->{m_weight}->set_text (sprintf "Max weight: %.1fkg", $stats->{+CS_STAT_WEIGHT_LIM} / 1000);
604 elmex 1.5
605 root 1.12 # TODO: replace by CS_STAT_RES_xxx constants
606 root 1.1 my %tbl = (
607     phys => 100,
608     magic => 101,
609     fire => 102,
610     elec => 103,
611     cold => 104,
612     conf => 105,
613     acid => 106,
614     drain => 107,
615     ghit => 108,
616     pois => 109,
617     slow => 110,
618     para => 111,
619     tund => 112,
620     fear => 113,
621     depl => 113,
622     deat => 115,
623     holyw => 116,
624 root 1.12 blind => 117,
625 root 1.1 );
626    
627 root 1.12 $STATWIDS->{"res_$_"}->set_text (sprintf "%d%", $stats->{$tbl{$_}})
628     for keys %tbl;
629 root 1.1 }
630    
631     my $METASERVER_ATIME;
632    
633     sub update_metaserver {
634     return if $METASERVER_ATIME > time;
635     $METASERVER_ATIME = time + 60;
636    
637     my $table = $METASERVER->{table};
638     $table->clear;
639     $table->add (0, 0, my $label = new CFClient::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
640    
641     my $buf;
642    
643     my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0;
644    
645     unless ($fh) {
646     $label->set_text ("unable to contact metaserver: $!");
647     return;
648     }
649    
650     Event->io (fd => $fh, poll => 'r', cb => sub {
651     my $res = sysread $fh, $buf, 8192, length $buf;
652    
653     if (!defined $res) {
654     $_[0]->w->cancel;
655     $label->set_text ("error while retrieving server list: $!");
656     } elsif ($res == 0) {
657     $_[0]->w->cancel;
658     status "server list retrieved";
659    
660     utf8::decode $buf if utf8::valid $buf;
661    
662     $table->clear;
663    
664     my @col = qw(Use #Users Host Uptime Version Description);
665     $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_])
666     for 0 .. $#col;
667    
668     my @align = qw(1 0 1 1 -1);
669    
670     my $y = 0;
671     for my $m (sort { $b->[3] <=> $a->[3] } map [split /\|/], split /\015?\012/, $buf) {
672     my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) = @$m;
673    
674     for ($desc) {
675     s/<br>/\n/gi;
676     s/<li>/\n· /gi;
677     s/<.*?>//sgi;
678     s/&/&amp;/g;
679     s/</&lt;/g;
680     s/>/&gt;/g;
681     }
682    
683     $uptime = sprintf "%dd %02d:%02d:%02d",
684     (int $m->[8] / 86400),
685     (int $m->[8] / 3600) % 24,
686     (int $m->[8] / 60) % 60,
687     $m->[8] % 60;
688    
689     $m = [$users, $host, $uptime, $version, $desc];
690    
691     $y++;
692    
693     $table->add (0, $y, new CFClient::UI::VBox children => [
694 root 1.18 (new CFClient::UI::Button text => "Use", on_activate => sub {
695 root 1.40 $HOST_ENTRY->set_text ($CFG->{host} = $host);
696     $METASERVER->toggle_visibility;
697 root 1.1 }),
698     (new CFClient::UI::Empty expand => 1),
699     ]);
700    
701     $table->add ($_ + 1, $y, new CFClient::UI::Label
702     ellipsise => 0, align => $align[$_], text => $m->[$_], fontsize => 0.8)
703     for 0 .. $#$m;
704     }
705     }
706     });
707     }
708    
709 root 1.40 sub metaserver_dialog {
710     my $dialog = new CFClient::UI::FancyFrame
711     title => "Server List",
712 root 1.41 name => 'metaserver_dialog',
713 root 1.40 x => 'center',
714     y => 'center',
715     child => (my $vbox = new CFClient::UI::VBox),
716     on_visibility_change => sub {
717     update_metaserver if $_[1];
718     },
719     ;
720    
721     $vbox->add ($dialog->{table} = new CFClient::UI::Table);
722    
723     $dialog
724     }
725    
726 root 1.1 sub server_setup {
727 root 1.49 my $vbox = new CFClient::UI::VBox;
728 elmex 1.19
729 root 1.1 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
730     $table->add (0, 2, new CFClient::UI::Label valign => 0, align => 1, text => "Host:Port");
731    
732     {
733     $table->add (1, 2, my $vbox = new CFClient::UI::VBox);
734    
735     $vbox->add (
736 root 1.40 $HOST_ENTRY = new CFClient::UI::Entry
737 root 1.1 expand => 1,
738     text => $CFG->{host},
739     tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
740 root 1.18 on_changed => sub {
741 root 1.1 my ($self, $value) = @_;
742     $CFG->{host} = $value;
743     }
744     );
745    
746     $METASERVER = metaserver_dialog;
747    
748 root 1.40 $vbox->add (new CFClient::UI::Button
749     expand => 1,
750     text => "Server List",
751     other => $METASERVER,
752 root 1.1 tooltip => "Show a list of available crossfire servers",
753 root 1.40 on_activate => sub { $METASERVER->toggle_visibility },
754 root 1.1 );
755     }
756    
757     $table->add (0, 4, new CFClient::UI::Label valign => 0, align => 1, text => "Username");
758     $table->add (1, 4, new CFClient::UI::Entry
759     text => $CFG->{user},
760     tooltip => "The name of your character on the server",
761 root 1.18 on_changed => sub {
762 root 1.1 my ($self, $value) = @_;
763     $CFG->{user} = $value;
764     }
765     );
766    
767     $table->add (0, 5, new CFClient::UI::Label valign => 0, align => 1, text => "Password");
768     $table->add (1, 5, new CFClient::UI::Entry
769     text => $CFG->{password},
770     hidden => 1,
771     tooltip => "The password for your character",
772 root 1.18 on_changed => sub {
773 root 1.1 my ($self, $value) = @_;
774     $CFG->{password} = $value;
775     }
776     );
777    
778     $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size");
779     $table->add (1, 7, new CFClient::UI::Slider
780 root 1.30 force_w => 100,
781 root 1.1 range => [$CFG->{mapsize}, 10, 100, 0, 1],
782     tooltip => "This is the size of the portion of the map update the server sends you. "
783     . "If you set this to a high value you will be able to see further, "
784     . "but you also increase bandwidth requirements and latency. "
785     . "This option is only used once at log-in.",
786 root 1.18 on_changed => sub {
787 root 1.1 my ($self, $value) = @_;
788    
789     $CFG->{mapsize} = $self->{range}[0] = $value = int $value;
790     },
791     );
792    
793     $table->add (0, 8, new CFClient::UI::Label valign => 0, align => 1, text => "Face Prefetch");
794     $table->add (1, 8, new CFClient::UI::CheckBox
795     state => $CFG->{face_prefetch},
796     tooltip => "<b>Background Image Prefetch</b>\n\n"
797     . "If enabled, the client automatically pre-fetches images from the server. "
798     . "This might increase or create lag, but increases the chances "
799     . "of faces being ready for display when you encounter them. "
800     . "It also uses up server bandwidth on every connect, "
801     . "so only set it if you really need to prefetch images. "
802     . "This option can be set and unset any time.",
803 root 1.18 on_changed => sub { $CFG->{face_prefetch} = $_[1] },
804 root 1.1 );
805    
806     $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Output-Count");
807     $table->add (1, 9, new CFClient::UI::Entry
808     text => $CFG->{output_count},
809     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
810 root 1.18 on_changed => sub { $CFG->{output_count} = $_[1] },
811 root 1.1 );
812    
813     $table->add (0, 10, new CFClient::UI::Label valign => 0, align => 1, text => "Output-Sync");
814     $table->add (1, 10, new CFClient::UI::Entry
815     text => $CFG->{output_sync},
816     tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
817 root 1.18 on_changed => sub { $CFG->{output_sync} = $_[1] },
818 root 1.1 );
819    
820     $table->add (1, 11, $LOGIN_BUTTON = new CFClient::UI::Button
821     expand => 1,
822     align => 0,
823     text => "Login",
824 root 1.18 on_activate => sub {
825 root 1.1 $CONN ? stop_game
826     : start_game;
827     },
828     );
829    
830 root 1.49 $table->add (0, 12, new CFClient::UI::Label valign => 0, align => 1, text => "Chat Command");
831     $table->add (1, 12, my $saycmd = new CFClient::UI::Entry
832     text => $CFG->{say_command},
833     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. "
834     . "Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
835     . "But you could also set it to <b>tell <i>playername</i></b> to only chat with that user.",
836     on_changed => sub {
837     my ($self, $value) = @_;
838     $CFG->{say_command} = $value;
839     }
840     );
841    
842     $vbox
843 root 1.1 }
844    
845     sub message_window {
846     my $window = new CFClient::UI::FancyFrame
847 elmex 1.16 name => "message_window",
848 root 1.1 title => "Messages",
849     border_bg => [1, 1, 1, 1],
850     bg => [0, 0, 0, 0.75],
851 root 1.30 x => "max",
852     y => 0,
853     force_w => $::WIDTH / 3,
854     force_h => $::HEIGHT / 5,
855 root 1.1 child => (my $vbox = new CFClient::UI::VBox);
856    
857     $vbox->add ($LOGVIEW);
858    
859     $vbox->add (my $input = new CFClient::UI::Entry
860     tooltip => "<b>Chat Box</b>. If you enter a text and press return/enter here, the current <i>communication command</i> "
861     . "from the client setup will be prepended (e.g. <b>shout</b>, <b>chat</b>...). "
862     . "If you prepend a slash (/), you will submit a command instead (similar to IRC). "
863     . "A better way to submit commands (and the occasional chat command) is often the map command completer.",
864 root 1.18 on_focus_in => sub {
865 root 1.1 my ($input, $prev_focus) = @_;
866    
867     delete $input->{refocus_map};
868    
869     if ($prev_focus == $MAPWIDGET && $input->{auto_activated}) {
870     $input->{refocus_map} = 1;
871     }
872     delete $input->{auto_activated};
873     },
874 root 1.18 on_activate => sub {
875 root 1.1 my ($input, $text) = @_;
876     $input->set_text ('');
877    
878 elmex 1.46 if ($text =~ /^\/(.*)/) {
879 root 1.1 $::CONN->user_send ($1);
880     } else {
881     my $say_cmd = $::CFG->{say_command} || 'say';
882     $::CONN->user_send ("$say_cmd $text");
883     }
884     if ($input->{refocus_map}) {
885     delete $input->{refocus_map};
886     $MAPWIDGET->focus_in
887     }
888     },
889 root 1.18 on_escape => sub {
890 root 1.1 $MAPWIDGET->focus_in
891     },
892     );
893    
894     $CONSOLE = {
895     window => $window,
896 root 1.30 input => $input,
897 root 1.1 };
898    
899     $window
900     }
901    
902     sub open_quit_dialog {
903     unless ($QUIT_DIALOG) {
904 root 1.30 $QUIT_DIALOG = new CFClient::UI::FancyFrame
905     x => "center",
906     y => "center",
907 root 1.55 z => 50,
908 root 1.30 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 root 1.55 z => 2,
1131 root 1.41 name => 'doc_browser',
1132     force_w => int $WIDTH * 7/8,
1133     force_h => int $HEIGHT * 7/8,
1134     title => "Documentation";
1135 root 1.1
1136     $win->add (my $vbox = new CFClient::UI::VBox);
1137    
1138     $vbox->add (my $buttons = new CFClient::UI::HBox);
1139     $vbox->add (my $viewer = new CFClient::UI::TextView expand => 1, fontsize => 0.8);
1140    
1141     for (
1142     [intro => "Introduction"],
1143     [manual => "Manual"],
1144     [command_help => "Commands"],
1145     [skill_help => "Skills"],
1146     ) {
1147     my ($pod, $label) = @$_;
1148    
1149     $buttons->add (new CFClient::UI::Button
1150     text => $label,
1151 root 1.18 on_activate => sub {
1152 root 1.56 my $pom = CFClient::load_pod CFClient::find_rcfile "pod/$pod.pod";
1153 root 1.1
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