ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.43
Committed: Fri Jun 2 16:27:28 2006 UTC (17 years, 11 months ago) by elmex
Branch: MAIN
Changes since 1.42: +74 -0 lines
Log Message:
implemented auto pickup dialog

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