ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.44
Committed: Fri Jun 2 16:32:12 2006 UTC (17 years, 11 months ago) by elmex
Branch: MAIN
Changes since 1.43: +3 -2 lines
Log Message:
autopickup layout changed

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     sub formsep {
563     reverse join ",", grep length, split /(...)/, reverse $_[0] * 1
564     }
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.24 if ($text =~ /^\/bind\s+(.*)$/) {
877 elmex 1.34 CFClient::Binder::open_binding_dialog (sub {
878     my ($mod, $sym) = @_;
879     $::CFG->{bindings}->{$mod}->{$sym} = [$1];
880     });
881 elmex 1.24 } elsif ($text =~ /^\/(.*)/) {
882 root 1.1 $::CONN->user_send ($1);
883     } else {
884     my $say_cmd = $::CFG->{say_command} || 'say';
885     $::CONN->user_send ("$say_cmd $text");
886     }
887     if ($input->{refocus_map}) {
888     delete $input->{refocus_map};
889     $MAPWIDGET->focus_in
890     }
891     },
892 root 1.18 on_escape => sub {
893 root 1.1 $MAPWIDGET->focus_in
894     },
895     );
896    
897     $CONSOLE = {
898     window => $window,
899 root 1.30 input => $input,
900 root 1.1 };
901    
902     $window
903     }
904    
905     sub open_quit_dialog {
906     unless ($QUIT_DIALOG) {
907 root 1.30 $QUIT_DIALOG = new CFClient::UI::FancyFrame
908     x => "center",
909     y => "center",
910     title => "Really Quit?",
911     ;
912 root 1.1
913     $QUIT_DIALOG->add (my $vb = new CFClient::UI::VBox expand => 1);
914    
915     $vb->add (new CFClient::UI::Label
916     text => "You should find a savebed and apply it first!",
917     max_w => $WIDTH * 0.25,
918     ellipsize => 0,
919     );
920     $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
921     $hb->add (new CFClient::UI::Button
922     text => "Ok",
923     expand => 1,
924 root 1.18 on_activate => sub { $QUIT_DIALOG->hide },
925 root 1.1 );
926     $hb->add (new CFClient::UI::Button
927     text => "Quit anyway",
928     expand => 1,
929 root 1.18 on_activate => sub { exit },
930 root 1.1 );
931 root 1.21 }
932 root 1.1
933 root 1.21 $QUIT_DIALOG->show;
934 root 1.1 }
935    
936 elmex 1.43 sub make_pickup_cfg_window {
937     $PICKUP_CFG = new CFClient::UI::FancyFrame
938 elmex 1.44 title => "Autopickup configuration",
939 elmex 1.43 x => "center",
940     y => "center",
941     force_w => $WIDTH * 3/10,
942     force_h => $HEIGHT * 9/10;
943    
944     my $tbl = new CFClient::UI::Table;
945     my $tblrow = 0;
946 elmex 1.44
947     $PICKUP_CFG->add (my $sw = new CFClient::UI::ScrolledWindow scrolled => $tbl, expand => 1);
948 elmex 1.43
949     for (
950     ["Enable (new) autopickup" => CFClient::Pickup::PU_NEWMODE],
951     ["Inhibit autopickup" => CFClient::Pickup::PU_INHIBIT],
952     ["Stop before pickup" => CFClient::Pickup::PU_STOP],
953     ["Debug autopickup" => CFClient::Pickup::PU_DEBUG],
954     ["Weapons"],
955     ["All weapons" => CFClient::Pickup::PU_ALLWEAPON],
956     ["Missile weapons" => CFClient::Pickup::PU_MISSILEWEAPON],
957     ["Bows" => CFClient::Pickup::PU_BOW],
958     ["Arrows" => CFClient::Pickup::PU_ARROW],
959     ["Armour"],
960     ["Helmets" => CFClient::Pickup::PU_HELMET],
961     ["Shields" => CFClient::Pickup::PU_SHIELD],
962     ["Body Armour" => CFClient::Pickup::PU_ARMOUR],
963     ["Boots" => CFClient::Pickup::PU_BOOTS],
964     ["Gloves" => CFClient::Pickup::PU_GLOVES],
965     ["Cloaks" => CFClient::Pickup::PU_CLOAK],
966     ["Readables"],
967     ["Spellbooks" => CFClient::Pickup::PU_SPELLBOOK],
968     ["Skillscrolls" => CFClient::Pickup::PU_SKILLSCROLL],
969     ["Normal Books/Scrolls" => CFClient::Pickup::PU_READABLES],
970     ["Misc"],
971     ["Food" => CFClient::Pickup::PU_FOOD],
972     ["Drinks" => CFClient::Pickup::PU_DRINK],
973     ["Valuables (Money, Gems)" => CFClient::Pickup::PU_VALUABLES],
974     ["Keys" => CFClient::Pickup::PU_KEY],
975     ["Magical Items" => CFClient::Pickup::PU_MAGICAL],
976     ["Potions" => CFClient::Pickup::PU_POTION],
977     ["Magic Devices" => CFClient::Pickup::PU_MAGIC_DEVICE],
978     ["Ignore cursed" => CFClient::Pickup::PU_NOT_CURSED],
979     ["Jewelery" => CFClient::Pickup::PU_JEWELS],
980     )
981     {
982     unless (defined $_->[1]) {
983     $tbl->add (0, $tblrow++, new CFClient::UI::Label text => $_->[0], align => 0);
984     } else {
985     my $mask = $_->[1];
986     $tbl->add (0, $tblrow, new CFClient::UI::Label text => $_->[0], align => -1);
987     $tbl->add (1, $tblrow++, new CFClient::UI::CheckBox
988     state => $CFG->{pickup} & $mask,
989     on_changed => sub {
990     my ($box, $value) = @_;
991     if ($value) {
992     $CFG->{pickup} |= $mask;
993     } else {
994     $CFG->{pickup} = $CFG->{pickup} & ~$mask;
995     }
996     $::CONN->send (sprintf "command pickup %u", $CFG->{pickup});
997     });
998     }
999     }
1000    
1001     $PICKUP_CFG
1002     }
1003    
1004 root 1.1 sub make_inventory_window {
1005 root 1.23 my $invwin = $INV_WINDOW = new CFClient::UI::FancyFrame
1006 root 1.32 x => "center",
1007     y => "center",
1008     force_w => $WIDTH * 9/10,
1009     force_h => $HEIGHT * 9/10,
1010     title => "Inventory",
1011 root 1.21 ;
1012 root 1.1
1013 root 1.21 $invwin->add (my $hb = new CFClient::UI::HBox homogeneous => 1);
1014 root 1.1
1015 root 1.21 $hb->add (my $vb1 = new CFClient::UI::VBox);
1016     $vb1->add (new CFClient::UI::Label align => 0, text => "Player");
1017     $vb1->add ($INV = new CFClient::UI::Inventory expand => 1);
1018 root 1.1
1019 root 1.21 $hb->add (my $vb2 = new CFClient::UI::VBox);
1020 elmex 1.17
1021 elmex 1.27 $vb2->add ($INV_RIGHT_HB = new CFClient::UI::HBox);
1022 elmex 1.14
1023 root 1.1 $vb2->add ($INVR = new CFClient::UI::Inventory expand => 1);
1024    
1025 elmex 1.27 # XXX: Call after $INVR = ... because set_opencont sets the items
1026     CFClient::Protocol::set_opencont ($::CONN, 0, "Floor");
1027    
1028 root 1.1 $invwin
1029     }
1030    
1031 elmex 1.38 sub make_spell_list {
1032     $SPELL_LIST = new CFClient::UI::SpellList
1033     force_w => $WIDTH * (9/10),
1034     force_h => $HEIGHT * (9/10);
1035     $SPELL_LIST
1036     }
1037    
1038 elmex 1.24 sub make_binding_window {
1039     my $binding_list = new CFClient::UI::VBox;
1040    
1041 elmex 1.34 my $refresh;
1042     $refresh = sub {
1043 elmex 1.24 $binding_list->clear ();
1044    
1045     for my $mod (keys %{$::CFG->{bindings}}) {
1046     for my $sym (keys %{$::CFG->{bindings}->{$mod}}) {
1047     my $cmds = $::CFG->{bindings}->{$mod}->{$sym};
1048     next unless ref $cmds eq 'ARRAY' and @$cmds > 0;
1049    
1050     my $lbl = join "; ", @$cmds;
1051 elmex 1.34 my $nam = CFClient::Binder::keycombo_to_name ($mod, $sym);
1052 elmex 1.24 $binding_list->add (my $hb = new CFClient::UI::HBox);
1053     $hb->add (new CFClient::UI::Button
1054 elmex 1.25 text => "delete",
1055 elmex 1.34 tooltip => "Deletes the binding",
1056 elmex 1.24 on_activate => sub {
1057     $binding_list->remove ($hb);
1058     delete $::CFG->{bindings}->{$mod}->{$sym};
1059     });
1060 elmex 1.34
1061     $hb->add (new CFClient::UI::Button
1062     text => "edit",
1063     tooltip => "Edits the binding",
1064     on_activate => sub {
1065     $::BIND_EDITOR->set_binding (
1066     $mod, $sym, $::CFG->{bindings}->{$mod}->{$sym},
1067     sub {
1068     my ($nmod, $nsym, $ncmds) = @_;
1069     delete $::CFG->{bindings}->{$mod}->{$sym};
1070     $::CFG->{bindings}->{$nmod}->{$nsym} = $ncmds;
1071     $refresh->();
1072     $::BIND_WINDOW->show;
1073     },
1074     sub {
1075     $::BIND_WINDOW->show;
1076     });
1077     $::BIND_EDITOR->show;
1078     $::BIND_WINDOW->hide;
1079     });
1080    
1081     $hb->add (new CFClient::UI::Label text => "(Key: $nam)");
1082 elmex 1.24 $hb->add (new CFClient::UI::Label text => $lbl, expand => 1);
1083     }
1084     }
1085     };
1086    
1087     $BIND_WINDOW = new CFClient::UI::FancyFrame
1088     title => "Bindings",
1089 root 1.30 x => "center",
1090     y => "center",
1091     def_w => int $WIDTH * 9/10,
1092     def_h => int $HEIGHT * 9/10,
1093 elmex 1.24 on_visibility_change => sub {
1094     my ($self, $visible) = @_;
1095 root 1.30 $refresh->() if $visible;
1096 elmex 1.24 };
1097    
1098 elmex 1.34 $BIND_WINDOW->add (my $vb = new CFClient::UI::VBox);
1099 elmex 1.35 $vb->add ($binding_list);
1100     $vb->add (my $hb = new CFClient::UI::HBox);
1101     $hb->add (new CFClient::UI::Button
1102 elmex 1.34 text => "record new",
1103 elmex 1.35 expand => 1,
1104 elmex 1.34 tooltip => "This button opens the binding editor with an empty binding.",
1105     on_activate => sub {
1106     $::BIND_EDITOR->set_binding (undef, undef, [],
1107     sub {
1108     my ($mod, $sym, $cmds) = @_;
1109     $::CFG->{bindings}->{$mod}->{$sym} = $cmds;
1110     $refresh->();
1111     $::BIND_WINDOW->show;
1112     },
1113     sub {
1114     $::BIND_WINDOW->show;
1115     });
1116     $::BIND_WINDOW->hide;
1117     $::BIND_EDITOR->show;
1118     },
1119     );
1120 elmex 1.35 $hb->add (new CFClient::UI::Button
1121     text => "close",
1122     tooltip => "Closes the binding window",
1123     expand => 1,
1124     on_activate => sub {
1125     $::BIND_WINDOW->hide;
1126     }
1127     );
1128    
1129 elmex 1.24 $refresh->();
1130     $BIND_WINDOW
1131     }
1132    
1133 root 1.1 sub make_help_window {
1134     my $win = new CFClient::UI::FancyFrame
1135 root 1.41 x => 'center',
1136     y => 'center',
1137     name => 'doc_browser',
1138     force_w => int $WIDTH * 7/8,
1139     force_h => int $HEIGHT * 7/8,
1140     title => "Documentation";
1141 root 1.1
1142     $win->add (my $vbox = new CFClient::UI::VBox);
1143    
1144     $vbox->add (my $buttons = new CFClient::UI::HBox);
1145     $vbox->add (my $viewer = new CFClient::UI::TextView expand => 1, fontsize => 0.8);
1146    
1147     for (
1148     [intro => "Introduction"],
1149     [manual => "Manual"],
1150     [command_help => "Commands"],
1151     [skill_help => "Skills"],
1152     ) {
1153     my ($pod, $label) = @$_;
1154    
1155     $buttons->add (new CFClient::UI::Button
1156     text => $label,
1157 root 1.18 on_activate => sub {
1158 root 1.1 my $parser = new Pod::POM;
1159     my $pom = $parser->parse_file (CFClient::find_rcfile "pod/$pod.pod");
1160    
1161     $viewer->clear;
1162    
1163     $viewer->add_paragraph ([1, 1, 1, 1], $_->[1], $_->[0])
1164     for @{ CFClient::pod_to_pango_list $pom };
1165    
1166     $viewer->set_offset (0);
1167     },
1168     );
1169     }
1170    
1171     $viewer->add_paragraph ([1, 1, 0, 1], "<big>Use one of the buttons above to display a document.</big>");
1172    
1173     $win
1174     }
1175    
1176     sub sdl_init {
1177     CFClient::SDL_Init
1178     and die "SDL::Init failed!\n";
1179     }
1180    
1181     sub video_init {
1182     sdl_init;
1183    
1184     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1185    
1186     my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1187    
1188     ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1189     $FULLSCREEN = $CFG->{fullscreen};
1190     $FAST = $CFG->{fast};
1191    
1192     CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
1193     or die "SDL_SetVideoMode failed: " . (CFClient::SDL_GetError) . "\n";
1194    
1195     $SDL_ACTIVE = 1;
1196     $LAST_REFRESH = time - 0.01;
1197    
1198 root 1.10 CFClient::OpenGL::init;
1199 root 1.1
1200     $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1201    
1202     $CFClient::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1203    
1204     #############################################################################
1205    
1206     if ($DEBUG_STATUS) {
1207     CFClient::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1208     } else {
1209     # create the widgets
1210    
1211 root 1.30 $DEBUG_STATUS = new CFClient::UI::Label
1212     padding => 0,
1213     z => 100,
1214     force_x => "max",
1215     force_y => 0;
1216 root 1.1 $DEBUG_STATUS->show;
1217 elmex 1.34
1218     $BIND_EDITOR = new CFClient::UI::BindEditor (x => "max", y => 0);
1219    
1220 root 1.1 $STATUSBOX = new CFClient::UI::Statusbox;
1221     $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", pri => -100, color => [1, 1, 1, 0.8]);
1222    
1223     (new CFClient::UI::Frame
1224     bg => [0, 0, 0, 0.4],
1225 root 1.30 force_x => 0,
1226     force_y => "max",
1227 root 1.1 child => $STATUSBOX,
1228     )->show;
1229    
1230     CFClient::UI::FancyFrame->new (
1231 root 1.42 title => "Mini Map",
1232     name => "mapmap",
1233 root 1.30 x => 0,
1234     y => $FONTSIZE + 8,
1235 root 1.1 border_bg => [1, 1, 1, 192/255],
1236     bg => [1, 1, 1, 0],
1237     child => ($MAPMAP = new CFClient::MapWidget::MapMap
1238     tooltip => "<b>Map</b>. On servers that support this feature, this will display an overview of the surrounding areas.",
1239     ),
1240     )->show;
1241    
1242     $MAPWIDGET = new CFClient::MapWidget;
1243     $MAPWIDGET->connect (activate_console => sub {
1244     my ($mapwidget, $preset) = @_;
1245    
1246     if ($CONSOLE) {
1247     $CONSOLE->{input}->{auto_activated} = 1;
1248     $CONSOLE->{input}->focus_in;
1249    
1250     if ($preset && $CONSOLE->{input}->get_text eq '') {
1251     $CONSOLE->{input}->set_text ($preset);
1252     }
1253     }
1254     });
1255     $MAPWIDGET->show;
1256     $MAPWIDGET->focus_in;
1257    
1258     $LOGVIEW = new CFClient::UI::TextView
1259     expand => 1,
1260     font => $FONT_FIXED,
1261     fontsize => $::CFG->{log_fontsize},
1262     can_hover => 1,
1263     can_events => 1,
1264     tooltip => "<b>Server Log</b>. This text viewer contains all the messages sent by the server.",
1265     ;
1266    
1267 root 1.30 $BUTTONBAR = new CFClient::UI::HBox x => 0, y => 0;
1268 root 1.1
1269     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Client Setup", other => client_setup,
1270     tooltip => "Toggles a dialog where you can configure various aspects of the client, such as graphics mode, performance, and audio options.");
1271     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Server Setup", other => server_setup,
1272     tooltip => "Toggles a dialog where you can configure the server to play on, your username, password and other server-related options.");
1273     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window,
1274     tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1275    
1276     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
1277    
1278     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Stats Window", other => make_stats_window,
1279     tooltip => "Toggles the statistics window, where all your Stats and Resistances are being displayed at all times.");
1280     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Inventory", other => make_inventory_window,
1281 elmex 1.36 tooltip => "Toggles the inventory window, where you can manage your loot (or treaures :)."
1282     ."You can also hit the Tab-key to show/hide the Inventory.");
1283 root 1.1
1284     $BUTTONBAR->add (new CFClient::UI::Button
1285     text => "Save Config",
1286     tooltip => "Saves the options chosen in the client setting, server settings and the window layout to be restored on later runs.",
1287 root 1.18 on_activate => sub {
1288 elmex 1.16 $::CFG->{layout} = CFClient::UI::get_layout;
1289 root 1.28 CFClient::write_cfg "$Crossfire::VARDIR/cfplusrc";
1290 root 1.1 status "Configuration Saved";
1291     },
1292     );
1293    
1294     $BUTTONBAR->add (new CFClient::UI::Flopper text => "Help!", other => make_help_window,
1295     tooltip => "View Documentation");
1296    
1297 elmex 1.24 $BUTTONBAR->add (new CFClient::UI::Flopper
1298     text => "Bindings",
1299     other => make_binding_window,
1300 elmex 1.35 tooltip =>
1301     "Lets you define, edit and delete bindings."
1302     ."There is a shortcut for making bindings: LCTRL+Insert opens the binding editor "
1303     ."with nothing set and the recording started. After doing the actions you "
1304     ."want to record press Insert and you will be asked to press a key-combo."
1305     ."After pressing the combo the binding will be saved automatically and the "
1306 elmex 1.38 ."binding editor closes");
1307    
1308     $BUTTONBAR->add (new CFClient::UI::Flopper
1309     text => "Spells",
1310     other => make_spell_list,
1311     tooltip => "The spell list");
1312 elmex 1.24
1313 elmex 1.43 $BUTTONBAR->add (new CFClient::UI::Flopper
1314     text => "Pickup",
1315     other => make_pickup_cfg_window,
1316     tooltip => "The pickup dialog");
1317    
1318    
1319 root 1.1 $BUTTONBAR->add (new CFClient::UI::Button
1320 root 1.18 text => "Quit",
1321     tooltip => "Terminates the program",
1322     on_activate => sub {
1323 root 1.1 if ($CONN) {
1324     open_quit_dialog;
1325     } else {
1326     exit;
1327     }
1328     },
1329     );
1330    
1331     $BUTTONBAR->show;
1332 root 1.30 $SERVER_SETUP->show;
1333 root 1.1
1334     $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1335     }
1336     }
1337    
1338     sub video_shutdown {
1339     undef $SDL_ACTIVE;
1340     }
1341    
1342     my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
1343     my $bgmusic;#TODO#hack#d#
1344    
1345     sub audio_channel_finished {
1346     my ($channel) = @_;
1347    
1348     #warn "channel $channel finished\n";#d#
1349     }
1350    
1351     sub audio_music_finished {
1352     return unless $CFG->{bgm_enable};
1353    
1354     # TODO: hack, do play loop and mood music
1355     $bgmusic = new_from_file CFClient::MixMusic CFClient::find_rcfile "music/$bgmusic[0]";
1356     $bgmusic->play (0);
1357    
1358     push @bgmusic, shift @bgmusic;
1359     }
1360    
1361     sub audio_init {
1362     if ($CFG->{audio_enable}) {
1363     if (open my $fh, "<", CFClient::find_rcfile "sounds/config") {
1364     $SDL_MIXER = !CFClient::Mix_OpenAudio;
1365    
1366     unless ($SDL_MIXER) {
1367     status "Unable to open sound device: there will be no sound";
1368     return;
1369     }
1370    
1371     CFClient::Mix_AllocateChannels 8;
1372     CFClient::MixMusic::volume $CFG->{bgm_volume} * 128;
1373    
1374     audio_music_finished;
1375    
1376     while (<$fh>) {
1377     next if /^\s*#/;
1378     next if /^\s*$/;
1379    
1380     my ($file, $volume, $event) = split /\s+/, $_, 3;
1381    
1382     push @SOUNDS, "$volume,$file";
1383    
1384     $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1385     my $chunk = new_from_file CFClient::MixChunk CFClient::find_rcfile "sounds/$file";
1386     $chunk->volume ($volume * 128 / 100);
1387     $chunk
1388     };
1389     }
1390     } else {
1391     status "unable to open sound config: $!";
1392     }
1393     }
1394     }
1395    
1396     sub audio_shutdown {
1397     CFClient::Mix_CloseAudio if $SDL_MIXER;
1398     undef $SDL_MIXER;
1399     @SOUNDS = ();
1400     %AUDIO_CHUNKS = ();
1401     }
1402    
1403     my %animate_object;
1404     my $animate_timer;
1405    
1406     my $fps = 9;
1407    
1408     my %demo;#d#
1409    
1410     sub force_refresh {
1411     $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1412 root 1.33 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1413 root 1.1
1414     $CFClient::UI::ROOT->draw;
1415    
1416     $WANT_REFRESH = 0;
1417     $CAN_REFRESH = 0;
1418     $LAST_REFRESH = $NOW;
1419    
1420     0 && do {
1421     # some weird model-drawing code, just a joke right now
1422     use CFClient::OpenGL;
1423    
1424     $demo{t}{eye_auv} ||= new_from_file CFClient::Texture "eye2.png" or die;
1425     $demo{t}{body_auv} ||= new_from_file CFClient::Texture "body_auv3.png" or die;
1426     $demo{r} ||= do {
1427     my $mod = Compress::LZF::sthaw do { local $/; open my $fh, "<:raw:perlio", "dread.lz3"; <$fh> };
1428     $mod->{v} = pack "f*", @{$mod->{v}};
1429     $_ = [scalar @$_, pack "S!*", @$_]
1430     for values %{$mod->{g}};
1431     $mod
1432     };
1433    
1434     my $r = $demo{r} or die;
1435    
1436     glDepthMask 1;
1437     glClear GL_DEPTH_BUFFER_BIT;
1438     glEnable GL_TEXTURE_2D;
1439     glEnable GL_DEPTH_TEST;
1440     glEnable GL_CULL_FACE;
1441     glShadeModel $::FAST ? GL_FLAT : GL_SMOOTH;
1442    
1443     glMatrixMode GL_PROJECTION;
1444     glLoadIdentity;
1445     glFrustum -1 * ($::WIDTH / $::HEIGHT), 1 * ($::WIDTH / $::HEIGHT), 1, -1, 1, 10000;
1446     #glOrtho 0, $::WIDTH, 0, $::HEIGHT, -10000, 10000;
1447     glMatrixMode GL_MODELVIEW;
1448     glLoadIdentity;
1449    
1450     glPushMatrix;
1451     glTranslate 0, 0, -800;
1452     glScale 1, -1, 1;
1453     glRotate $NOW * 1000 % 36000 / 5, 0, 1, 0;
1454     glRotate $NOW * 1000 % 36000 / 6, 1, 0, 0;
1455     glRotate $NOW * 1000 % 36000 / 7, 0, 0, 1;
1456     glScale 50, 50, 50;
1457    
1458     glInterleavedArrays GL_T2F_N3F_V3F, 0, $r->{v};
1459     while (my ($k, $v) = each %{$r->{g}}) {
1460     glBindTexture GL_TEXTURE_2D, ($demo{t}{$k}{name} or die);
1461     glDrawElements GL_TRIANGLES, $v->[0], GL_UNSIGNED_SHORT, $v->[1];
1462     }
1463    
1464     glPopMatrix;
1465    
1466     glShadeModel GL_FLAT;
1467     glDisable GL_DEPTH_TEST;
1468     glDisable GL_TEXTURE_2D;
1469     glDepthMask 0;
1470    
1471     $WANT_REFRESH++;
1472     };
1473    
1474     CFClient::SDL_GL_SwapBuffers;
1475     }
1476    
1477     my $refresh_watcher = Event->timer (after => 0, hard => 1, interval => 1 / $MAX_FPS, cb => sub {
1478     $NOW = time;
1479    
1480     ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1481     for CFClient::SDL_PollEvent;
1482    
1483     if (%animate_object) {
1484     $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1485     $WANT_REFRESH++;
1486     }
1487    
1488     if ($WANT_REFRESH) {
1489     force_refresh;
1490     } else {
1491     $CAN_REFRESH = 1;
1492     }
1493     });
1494    
1495     sub animation_start {
1496     my ($widget) = @_;
1497     $animate_object{$widget} = $widget;
1498     }
1499    
1500     sub animation_stop {
1501     my ($widget) = @_;
1502     delete $animate_object{$widget};
1503     }
1504    
1505     # check once/second for faces that need to be prefetched
1506     # this should, of course, only run on demand, but
1507     # SDL forces worse things on us....
1508    
1509     Event->timer (after => 1, interval => 0.25, cb => sub {
1510     $CONN->face_prefetch
1511     if $CONN;
1512     });
1513    
1514     %SDL_CB = (
1515     CFClient::SDL_QUIT => sub {
1516     Event::unloop -1;
1517     },
1518     CFClient::SDL_VIDEORESIZE => sub {
1519     },
1520     CFClient::SDL_VIDEOEXPOSE => sub {
1521     CFClient::UI::full_refresh;
1522     },
1523     CFClient::SDL_ACTIVEEVENT => sub {
1524     # printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d#
1525     },
1526     CFClient::SDL_KEYDOWN => sub {
1527     if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) {
1528     # alt-enter
1529     video_shutdown;
1530     $CFG->{fullscreen} = !$CFG->{fullscreen};
1531     video_init;
1532     } else {
1533     CFClient::UI::feed_sdl_key_down_event ($_[0]);
1534     }
1535     },
1536     CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event,
1537     CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event,
1538     CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event,
1539     CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event,
1540     CFClient::SDL_USEREVENT => sub {
1541     if ($_[0]{code} == 1) {
1542     audio_channel_finished $_[0]{data1};
1543     } elsif ($_[0]{code} == 0) {
1544     audio_music_finished;
1545     }
1546     },
1547     );
1548    
1549     #############################################################################
1550    
1551     $SIG{INT} = $SIG{TERM} = sub { exit };
1552    
1553     {
1554 root 1.29 local $SIG{__DIE__} = sub { CFClient::fatal $_[0] if defined $^S && !$^S };
1555 root 1.1
1556 root 1.28 CFClient::read_cfg "$Crossfire::VARDIR/cfplusrc";
1557 elmex 1.16 CFClient::UI::set_layout ($::CFG->{layout});
1558 root 1.1
1559     my %DEF_CFG = (
1560     sdl_mode => 0,
1561     width => 640,
1562     height => 480,
1563     fullscreen => 0,
1564     fast => 0,
1565     map_scale => 1,
1566     fow_enable => 1,
1567     fow_intensity => 0.45,
1568     fow_smooth => 0,
1569     gui_fontsize => 1,
1570     log_fontsize => 1,
1571     gauge_fontsize=> 1,
1572     gauge_size => 0.35,
1573     stat_fontsize => 1,
1574     mapsize => 100,
1575     host => "crossfire.schmorp.de",
1576     say_command => 'say',
1577     audio_enable => 1,
1578     bgm_enable => 1,
1579     bgm_volume => 0.25,
1580     face_prefetch => 0,
1581     output_sync => 1,
1582     output_count => 1,
1583     );
1584    
1585     while (my ($k, $v) = each %DEF_CFG) {
1586     $CFG->{$k} = $v unless exists $CFG->{$k};
1587     }
1588    
1589     sdl_init;
1590    
1591     @SDL_MODES = reverse
1592     grep $_->[0] >= 640 && $_->[1] >= 480,
1593     CFClient::SDL_ListModes;
1594    
1595     @SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
1596    
1597     $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
1598    
1599     {
1600     my @fonts = map CFClient::find_rcfile "fonts/$_", qw(
1601     DejaVuSans.ttf
1602     DejaVuSansMono.ttf
1603     DejaVuSans-Bold.ttf
1604     DejaVuSansMono-Bold.ttf
1605     DejaVuSans-Oblique.ttf
1606     DejaVuSansMono-Oblique.ttf
1607     DejaVuSans-BoldOblique.ttf
1608     DejaVuSansMono-BoldOblique.ttf
1609     );
1610    
1611     CFClient::add_font $_ for @fonts;
1612    
1613     CFClient::pango_init;
1614    
1615     $FONT_PROP = new_from_file CFClient::Font $fonts[0];
1616     $FONT_FIXED = new_from_file CFClient::Font $fonts[1];
1617    
1618     $FONT_PROP->make_default;
1619     }
1620    
1621     # compare mono (ft) vs. rgba (cairo)
1622     # ft - 1.8s, cairo 3s, even in alpha-only mode
1623     # for my $rgba (0..1) {
1624     # my $t1 = Time::HiRes::time;
1625     # for (1..1000) {
1626     # my $layout = CFClient::Layout->new ($rgba);
1627     # $layout->set_text ("hallo" x 100);
1628     # $layout->render;
1629     # }
1630     # my $t2 = Time::HiRes::time;
1631     # warn $t2-$t1;
1632     # }
1633    
1634     video_init;
1635     audio_init;
1636     }
1637    
1638     Event::loop;
1639    
1640     END { CFClient::SDL_Quit }
1641    
1642     =head1 NAME
1643    
1644 root 1.28 cfplus - A Crossfire+ and Crossfire game client
1645 root 1.1
1646     =head1 SYNOPSIS
1647    
1648     Just run it - no commandline arguments are supported.
1649    
1650     =head1 USAGE
1651    
1652 root 1.28 cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used
1653 root 1.1 fullscreen and interactively.
1654    
1655 root 1.39 =head1 DEBUGGING
1656    
1657    
1658     CFPLUS_DEBUG - environment variable
1659    
1660     1 draw borders around widgets
1661     2 add low-level widget info to tooltips
1662     4 show fps
1663     8 suppress tooltips
1664    
1665 root 1.1 =head1 AUTHOR
1666    
1667     Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
1668    
1669    
1670