ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/cfplus
Revision: 1.50
Committed: Sat Jun 3 02:32:35 2006 UTC (17 years, 11 months ago) by root
Branch: MAIN
Changes since 1.49: +3 -2 lines
Log Message:
put dialog settings into a scrolled window, improve scrolled window and notebook

File Contents

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