ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.51
Committed: Sat Jun 3 21:59:55 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.50: +48 -51 lines
Log Message:
updated pickup setup

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.49 $SETUP_DIALOG->show;
180 root 1.23 $INV_WINDOW->hide;
181     $LOGVIEW->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 elmex 1.34 });
1106 root 1.49 $SETUP_DIALOG->hide;
1107 elmex 1.34 $::BIND_EDITOR->show;
1108     },
1109     );
1110 root 1.49
1111 elmex 1.35 $hb->add (new CFClient::UI::Button
1112     text => "close",
1113     tooltip => "Closes the binding window",
1114     expand => 1,
1115     on_activate => sub {
1116 root 1.49 $SETUP_DIALOG->hide;
1117 elmex 1.35 }
1118     );
1119    
1120 elmex 1.24 $refresh->();
1121 root 1.49
1122     $vb
1123 elmex 1.24 }
1124    
1125 root 1.1 sub make_help_window {
1126     my $win = new CFClient::UI::FancyFrame
1127 root 1.41 x => 'center',
1128     y => 'center',
1129     name => 'doc_browser',
1130     force_w => int $WIDTH * 7/8,
1131     force_h => int $HEIGHT * 7/8,
1132     title => "Documentation";
1133 root 1.1
1134     $win->add (my $vbox = new CFClient::UI::VBox);
1135    
1136     $vbox->add (my $buttons = new CFClient::UI::HBox);
1137     $vbox->add (my $viewer = new CFClient::UI::TextView expand => 1, fontsize => 0.8);
1138    
1139     for (
1140     [intro => "Introduction"],
1141     [manual => "Manual"],
1142     [command_help => "Commands"],
1143     [skill_help => "Skills"],
1144     ) {
1145     my ($pod, $label) = @$_;
1146    
1147     $buttons->add (new CFClient::UI::Button
1148     text => $label,
1149 root 1.18 on_activate => sub {
1150 root 1.1 my $parser = new Pod::POM;
1151     my $pom = $parser->parse_file (CFClient::find_rcfile "pod/$pod.pod");
1152    
1153     $viewer->clear;
1154    
1155     $viewer->add_paragraph ([1, 1, 1, 1], $_->[1], $_->[0])
1156     for @{ CFClient::pod_to_pango_list $pom };
1157    
1158     $viewer->set_offset (0);
1159     },
1160     );
1161     }
1162    
1163     $viewer->add_paragraph ([1, 1, 0, 1], "<big>Use one of the buttons above to display a document.</big>");
1164    
1165     $win
1166     }
1167    
1168     sub sdl_init {
1169     CFClient::SDL_Init
1170     and die "SDL::Init failed!\n";
1171     }
1172    
1173     sub video_init {
1174     sdl_init;
1175    
1176     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1177    
1178     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1179    
1180     ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1181     $FULLSCREEN = $CFG->{fullscreen};
1182     $FAST = $CFG->{fast};
1183    
1184     CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
1185     or die "SDL_SetVideoMode failed: " . (CFClient::SDL_GetError) . "\n";
1186    
1187     $SDL_ACTIVE = 1;
1188     $LAST_REFRESH = time - 0.01;
1189    
1190 root 1.10 CFClient::OpenGL::init;
1191 root 1.1
1192     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1193    
1194     $CFClient::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1195    
1196     #############################################################################
1197    
1198     if ($DEBUG_STATUS) {
1199     CFClient::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1200     } else {
1201     # create the widgets
1202    
1203 root 1.30 $DEBUG_STATUS = new CFClient::UI::Label
1204     padding => 0,
1205     z => 100,
1206     force_x => "max",
1207     force_y => 0;
1208 root 1.1 $DEBUG_STATUS->show;
1209 elmex 1.34
1210     $BIND_EDITOR = new CFClient::UI::BindEditor (x => "max", y => 0);
1211    
1212 root 1.1 $STATUSBOX = new CFClient::UI::Statusbox;
1213     $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", pri => -100, color => [1, 1, 1, 0.8]);
1214    
1215     (new CFClient::UI::Frame
1216     bg => [0, 0, 0, 0.4],
1217 root 1.30 force_x => 0,
1218     force_y => "max",
1219 root 1.1 child => $STATUSBOX,
1220     )->show;
1221    
1222     CFClient::UI::FancyFrame->new (
1223 root 1.47 title => "Map",
1224 root 1.42 name => "mapmap",
1225 root 1.30 x => 0,
1226     y => $FONTSIZE + 8,
1227 root 1.1 border_bg => [1, 1, 1, 192/255],
1228     bg => [1, 1, 1, 0],
1229     child => ($MAPMAP = new CFClient::MapWidget::MapMap
1230     tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
1231     ),
1232     )->show;
1233    
1234     $MAPWIDGET = new CFClient::MapWidget;
1235     $MAPWIDGET->connect (activate_console => sub {
1236     my ($mapwidget, $preset) = @_;
1237    
1238     if ($CONSOLE) {
1239     $CONSOLE->{input}->{auto_activated} = 1;
1240     $CONSOLE->{input}->focus_in;
1241    
1242     if ($preset && $CONSOLE->{input}->get_text eq '') {
1243     $CONSOLE->{input}->set_text ($preset);
1244     }
1245     }
1246     });
1247     $MAPWIDGET->show;
1248     $MAPWIDGET->focus_in;
1249    
1250     $LOGVIEW = new CFClient::UI::TextView
1251     expand => 1,
1252     font => $FONT_FIXED,
1253     fontsize => $::CFG->{log_fontsize},
1254     can_hover => 1,
1255     can_events => 1,
1256     tooltip => "<b>Server Log</b>. This text viewer contains all the messages sent by the server.",
1257     ;
1258    
1259 root 1.49 $SETUP_DIALOG = new CFClient::UI::FancyFrame
1260     title => "Setup",
1261     name => "setup_dialog",
1262     x => 'center',
1263     y => 'center',
1264     force_w => $::WIDTH * 0.6,
1265     force_h => $::HEIGHT * 0.6,
1266     ;
1267    
1268 root 1.50 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFClient::UI::Notebook expand => 1, debug => 1,
1269     filter => new CFClient::UI::ScrolledWindow xxx => 1, expand => 1, scroll_y => 1);
1270 root 1.49
1271     $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup,
1272     "Configure the server to play on, your username, password and other server-related options.");
1273     $SETUP_NOTEBOOK->add (Pickup => autopickup_setup,
1274     "Configure autopicking stetings, i.e. which items you will pick up automatically when walking over them.");
1275     $SETUP_NOTEBOOK->add (Graphics => graphics_setup,
1276     "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1277     $SETUP_NOTEBOOK->add (Audio => audio_setup,
1278     "Configure the use of audio, sound effects and background music.");
1279     $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1280     "Lets you define, edit and delete bindings."
1281     . "There is a shortcut for making bindings: Left Control + Insert opens the binding editor "
1282     . "with nothing set and the recording started. After doing the actions you "
1283     . "want to record press Insert and you will be asked to press a key-combo."
1284     . "After pressing the combo the binding will be saved automatically and the "
1285     . "binding editor closes");
1286     $SETUP_NOTEBOOK->add (Spells => $SETUP_SPELLS = spell_setup,
1287     "Displays all spells you have and lets you edit keyboard shortcuts for them.");
1288    
1289 root 1.30 $BUTTONBAR = new CFClient::UI::HBox x => 0, y => 0;
1290 root 1.1
1291 root 1.49 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1292     tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1293    
1294 root 1.1 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window,
1295     tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1296    
1297     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
1298    
1299     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Stats Window", other => make_stats_window,
1300     tooltip => "Toggles the statistics window, where all your Stats and Resistances are being displayed at all times.");
1301     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Inventory", other => make_inventory_window,
1302 root 1.51 tooltip => "Toggles the inventory window, where you can manage your loot (or treasures :). "
1303 root 1.50 ."You can also hit the <b>Tab</b>-key to show/hide the Inventory.");
1304 root 1.1
1305     $BUTTONBAR->add (new CFClient::UI::Button
1306     text => "Save Config",
1307     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
1308 root 1.18 on_activate => sub {
1309 elmex 1.16 $::CFG->{layout} = CFClient::UI::get_layout;
1310 root 1.28 CFClient::write_cfg "$Crossfire::VARDIR/cfplusrc";
1311 root 1.1 status "Configuration Saved";
1312     },
1313     );
1314    
1315     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Help!", other => make_help_window,
1316     tooltip => "View Documentation");
1317    
1318     $BUTTONBAR->add (new CFClient::UI::Button
1319 root 1.18 text => "Quit",
1320     tooltip => "Terminates the program",
1321     on_activate => sub {
1322 root 1.1 if ($CONN) {
1323     open_quit_dialog;
1324     } else {
1325     exit;
1326     }
1327     },
1328     );
1329    
1330     $BUTTONBAR->show;
1331 root 1.49 $SETUP_DIALOG->show;
1332     }
1333 root 1.1
1334 root 1.49 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1335 root 1.1 }
1336    
1337     sub video_shutdown {
1338     undef $SDL_ACTIVE;
1339     }
1340    
1341     my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
1342     my $bgmusic;#TODO#hack#d#
1343    
1344     sub audio_channel_finished {
1345     my ($channel) = @_;
1346    
1347     #warn "channel $channel finished\n";#d#
1348     }
1349    
1350     sub audio_music_finished {
1351     return unless $CFG->{bgm_enable};
1352    
1353     # TODO: hack, do play loop and mood music
1354     $bgmusic = new_from_file CFClient::MixMusic CFClient::find_rcfile "music/$bgmusic[0]";
1355     $bgmusic->play (0);
1356    
1357     push @bgmusic, shift @bgmusic;
1358     }
1359    
1360     sub audio_init {
1361     if ($CFG->{audio_enable}) {
1362     if (open my $fh, "<", CFClient::find_rcfile "sounds/config") {
1363     $SDL_MIXER = !CFClient::Mix_OpenAudio;
1364    
1365     unless ($SDL_MIXER) {
1366     status "Unable to open sound device: there will be no sound";
1367     return;
1368     }
1369    
1370     CFClient::Mix_AllocateChannels 8;
1371     CFClient::MixMusic::volume $CFG->{bgm_volume} * 128;
1372    
1373     audio_music_finished;
1374    
1375     while (<$fh>) {
1376     next if /^\s*#/;
1377     next if /^\s*$/;
1378    
1379     my ($file, $volume, $event) = split /\s+/, $_, 3;
1380    
1381     push @SOUNDS, "$volume,$file";
1382    
1383     $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1384     my $chunk = new_from_file CFClient::MixChunk CFClient::find_rcfile "sounds/$file";
1385     $chunk->volume ($volume * 128 / 100);
1386     $chunk
1387     };
1388     }
1389     } else {
1390     status "unable to open sound config: $!";
1391     }
1392     }
1393     }
1394    
1395     sub audio_shutdown {
1396     CFClient::Mix_CloseAudio if $SDL_MIXER;
1397     undef $SDL_MIXER;
1398     @SOUNDS = ();
1399     %AUDIO_CHUNKS = ();
1400     }
1401    
1402     my %animate_object;
1403     my $animate_timer;
1404    
1405     my $fps = 9;
1406    
1407     my %demo;#d#
1408    
1409     sub force_refresh {
1410     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1411 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1412 root 1.1
1413     $CFClient::UI::ROOT->draw;
1414    
1415     $WANT_REFRESH = 0;
1416     $CAN_REFRESH = 0;
1417     $LAST_REFRESH = $NOW;
1418    
1419     0 && do {
1420     # some weird model-drawing code, just a joke right now
1421     use CFClient::OpenGL;
1422    
1423     $demo{t}{eye_auv} ||= new_from_file CFClient::Texture "eye2.png" or die;
1424     $demo{t}{body_auv} ||= new_from_file CFClient::Texture "body_auv3.png" or die;
1425     $demo{r} ||= do {
1426     my $mod = Compress::LZF::sthaw do { local $/; open my $fh, "<:raw:perlio", "dread.lz3"; <$fh> };
1427     $mod->{v} = pack "f*", @{$mod->{v}};
1428     $_ = [scalar @$_, pack "S!*", @$_]
1429     for values %{$mod->{g}};
1430     $mod
1431     };
1432    
1433     my $r = $demo{r} or die;
1434    
1435     glDepthMask 1;
1436     glClear GL_DEPTH_BUFFER_BIT;
1437     glEnable GL_TEXTURE_2D;
1438     glEnable GL_DEPTH_TEST;
1439     glEnable GL_CULL_FACE;
1440     glShadeModel $::FAST ? GL_FLAT : GL_SMOOTH;
1441    
1442     glMatrixMode GL_PROJECTION;
1443     glLoadIdentity;
1444     glFrustum -1 * ($::WIDTH / $::HEIGHT), 1 * ($::WIDTH / $::HEIGHT), 1, -1, 1, 10000;
1445     #glOrtho 0, $::WIDTH, 0, $::HEIGHT, -10000, 10000;
1446     glMatrixMode GL_MODELVIEW;
1447     glLoadIdentity;
1448    
1449     glPushMatrix;
1450     glTranslate 0, 0, -800;
1451     glScale 1, -1, 1;
1452     glRotate $NOW * 1000 % 36000 / 5, 0, 1, 0;
1453     glRotate $NOW * 1000 % 36000 / 6, 1, 0, 0;
1454     glRotate $NOW * 1000 % 36000 / 7, 0, 0, 1;
1455     glScale 50, 50, 50;
1456    
1457     glInterleavedArrays GL_T2F_N3F_V3F, 0, $r->{v};
1458     while (my ($k, $v) = each %{$r->{g}}) {
1459     glBindTexture GL_TEXTURE_2D, ($demo{t}{$k}{name} or die);
1460     glDrawElements GL_TRIANGLES, $v->[0], GL_UNSIGNED_SHORT, $v->[1];
1461     }
1462    
1463     glPopMatrix;
1464    
1465     glShadeModel GL_FLAT;
1466     glDisable GL_DEPTH_TEST;
1467     glDisable GL_TEXTURE_2D;
1468     glDepthMask 0;
1469    
1470     $WANT_REFRESH++;
1471     };
1472    
1473     CFClient::SDL_GL_SwapBuffers;
1474     }
1475    
1476 root 1.49 my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1477 root 1.1 $NOW = time;
1478    
1479     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1480     for CFClient::SDL_PollEvent;
1481    
1482     if (%animate_object) {
1483     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1484     $WANT_REFRESH++;
1485     }
1486    
1487     if ($WANT_REFRESH) {
1488     force_refresh;
1489     } else {
1490     $CAN_REFRESH = 1;
1491     }
1492     });
1493    
1494     sub animation_start {
1495     my ($widget) = @_;
1496     $animate_object{$widget} = $widget;
1497     }
1498    
1499     sub animation_stop {
1500     my ($widget) = @_;
1501     delete $animate_object{$widget};
1502     }
1503    
1504     # check once/second for faces that need to be prefetched
1505     # this should, of course, only run on demand, but
1506     # SDL forces worse things on us....
1507    
1508     Event->timer (after => 1, interval => 0.25, cb => sub {
1509     $CONN->face_prefetch
1510     if $CONN;
1511     });
1512    
1513     %SDL_CB = (
1514     CFClient::SDL_QUIT => sub {
1515     Event::unloop -1;
1516     },
1517     CFClient::SDL_VIDEORESIZE => sub {
1518     },
1519     CFClient::SDL_VIDEOEXPOSE => sub {
1520     CFClient::UI::full_refresh;
1521     },
1522     CFClient::SDL_ACTIVEEVENT => sub {
1523     # printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d#
1524     },
1525     CFClient::SDL_KEYDOWN => sub {
1526     if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) {
1527     # alt-enter
1528     video_shutdown;
1529     $CFG->{fullscreen} = !$CFG->{fullscreen};
1530     video_init;
1531     } else {
1532     CFClient::UI::feed_sdl_key_down_event ($_[0]);
1533     }
1534     },
1535     CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event,
1536     CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event,
1537     CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event,
1538     CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event,
1539     CFClient::SDL_USEREVENT => sub {
1540     if ($_[0]{code} == 1) {
1541     audio_channel_finished $_[0]{data1};
1542     } elsif ($_[0]{code} == 0) {
1543     audio_music_finished;
1544     }
1545     },
1546     );
1547    
1548     #############################################################################
1549    
1550     $SIG{INT} = $SIG{TERM} = sub { exit };
1551    
1552     {
1553 root 1.49 local $SIG{__DIE__} = sub {
1554     return unless defined $^S && !$^S;
1555     Carp::confess $_[1];#d#TODO: remove when stable
1556     CFClient::fatal $_[0];
1557     };
1558 root 1.1
1559 root 1.28 CFClient::read_cfg "$Crossfire::VARDIR/cfplusrc";
1560 elmex 1.16 CFClient::UI::set_layout ($::CFG->{layout});
1561 root 1.1
1562     my %DEF_CFG = (
1563     sdl_mode => 0,
1564     width => 640,
1565     height => 480,
1566     fullscreen => 0,
1567     fast => 0,
1568     map_scale => 1,
1569     fow_enable => 1,
1570     fow_intensity => 0.45,
1571     fow_smooth => 0,
1572     gui_fontsize => 1,
1573     log_fontsize => 1,
1574     gauge_fontsize=> 1,
1575     gauge_size => 0.35,
1576     stat_fontsize => 1,
1577     mapsize => 100,
1578     host => "crossfire.schmorp.de",
1579     say_command => 'say',
1580     audio_enable => 1,
1581     bgm_enable => 1,
1582     bgm_volume => 0.25,
1583     face_prefetch => 0,
1584     output_sync => 1,
1585     output_count => 1,
1586     );
1587    
1588     while (my ($k, $v) = each %DEF_CFG) {
1589     $CFG->{$k} = $v unless exists $CFG->{$k};
1590     }
1591    
1592     sdl_init;
1593    
1594     @SDL_MODES = reverse
1595     grep $_->[0] >= 640 && $_->[1] >= 480,
1596     CFClient::SDL_ListModes;
1597    
1598     @SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
1599    
1600     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
1601    
1602     {
1603     my @fonts = map CFClient::find_rcfile "fonts/$_", qw(
1604     DejaVuSans.ttf
1605     DejaVuSansMono.ttf
1606     DejaVuSans-Bold.ttf
1607     DejaVuSansMono-Bold.ttf
1608     DejaVuSans-Oblique.ttf
1609     DejaVuSansMono-Oblique.ttf
1610     DejaVuSans-BoldOblique.ttf
1611     DejaVuSansMono-BoldOblique.ttf
1612     );
1613    
1614     CFClient::add_font $_ for @fonts;
1615    
1616     CFClient::pango_init;
1617    
1618     $FONT_PROP = new_from_file CFClient::Font $fonts[0];
1619     $FONT_FIXED = new_from_file CFClient::Font $fonts[1];
1620    
1621     $FONT_PROP->make_default;
1622     }
1623    
1624     # compare mono (ft) vs. rgba (cairo)
1625     # ft - 1.8s, cairo 3s, even in alpha-only mode
1626     # for my $rgba (0..1) {
1627     # my $t1 = Time::HiRes::time;
1628     # for (1..1000) {
1629     # my $layout = CFClient::Layout->new ($rgba);
1630     # $layout->set_text ("hallo" x 100);
1631     # $layout->render;
1632     # }
1633     # my $t2 = Time::HiRes::time;
1634     # warn $t2-$t1;
1635     # }
1636    
1637     video_init;
1638     audio_init;
1639     }
1640    
1641     Event::loop;
1642    
1643     END { CFClient::SDL_Quit }
1644    
1645     =head1 NAME
1646    
1647 root 1.28 cfplus - A Crossfire+ and Crossfire game client
1648 root 1.1
1649     =head1 SYNOPSIS
1650    
1651     Just run it - no commandline arguments are supported.
1652    
1653     =head1 USAGE
1654    
1655 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
1656 root 1.1 fullscreen and interactively.
1657    
1658 root 1.39 =head1 DEBUGGING
1659    
1660    
1661     CFPLUS_DEBUG - environment variable
1662    
1663     1 draw borders around widgets
1664     2 add low-level widget info to tooltips
1665     4 show fps
1666     8 suppress tooltips
1667    
1668 root 1.1 =head1 AUTHOR
1669    
1670     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
1671    
1672    
1673