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