ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/pclient
(Generate patch)

Comparing deliantra/Deliantra-Client/bin/pclient (file contents):
Revision 1.168 by root, Sun Apr 23 21:47:32 2006 UTC vs.
Revision 1.224 by root, Wed May 17 15:18:57 2006 UTC

1#!/opt/bin/perl 1#!/opt/bin/perl
2 2
3use strict; 3use strict;
4use utf8; 4use utf8;
5 5
6# do things only needed for single-binary version (par)
7BEGIN {
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
26unshift @INC, $ENV{PAR_TEMP}
27 if %PAR::LibCache;
28
6use Time::HiRes 'time'; 29use Time::HiRes 'time';
30use Pod::POM;
7use Event; 31use Event;
8 32
9use Crossfire; 33use Crossfire;
10use Crossfire::Protocol; 34use Crossfire::Protocol;
11 35
13 37
14use CFClient; 38use CFClient;
15use CFClient::UI; 39use CFClient::UI;
16use CFClient::MapWidget; 40use CFClient::MapWidget;
17 41
42$Event::DIED = sub {
43 # TODO: display dialog box or so
44 CFClient::error $_[1];
45};
46
47#$SIG{__WARN__} = sub { Carp::cluck $_[0] };#d#
48
18our $VERSION = '0.1'; 49our $VERSION = '0.1';
19 50
20my $MAX_FPS = 60; 51my $MAX_FPS = 60;
21my $MIN_FPS = 5; # unused as of yet 52my $MIN_FPS = 5; # unused as of yet
22 53
30our $NOW; 61our $NOW;
31 62
32our $CFG; 63our $CFG;
33our $CONN; 64our $CONN;
34our $FAST; # fast, low-quality mode, possibly useful for software-rendering 65our $FAST; # fast, low-quality mode, possibly useful for software-rendering
66
67our $WANT_REFRESH;
68our $CAN_REFRESH;
35 69
36our @SDL_MODES; 70our @SDL_MODES;
37our $WIDTH; 71our $WIDTH;
38our $HEIGHT; 72our $HEIGHT;
39our $FULLSCREEN; 73our $FULLSCREEN;
41 75
42our $FONT_PROP; 76our $FONT_PROP;
43our $FONT_FIXED; 77our $FONT_FIXED;
44 78
45our $MAP; 79our $MAP;
80our $MAPMAP;
46our $MAPWIDGET; 81our $MAPWIDGET;
47our $BUTTONBAR; 82our $BUTTONBAR;
48our $LOGVIEW; 83our $LOGVIEW;
49our $CONSOLE; 84our $CONSOLE;
50our $METASERVER; 85our $METASERVER;
86our $LOGIN_BUTTON;
51 87
88our $FLOORBOX;
52our $GAUGES; 89our $GAUGES;
53our $STATWIDS; 90our $STATWIDS;
54 91
55our $SDL_ACTIVE; 92our $SDL_ACTIVE;
56our %SDL_CB; 93our %SDL_CB;
58our $SDL_MIXER; 95our $SDL_MIXER;
59our @SOUNDS; # event => file mapping 96our @SOUNDS; # event => file mapping
60our %AUDIO_CHUNKS; # audio files 97our %AUDIO_CHUNKS; # audio files
61 98
62our $ALT_ENTER_MESSAGE; 99our $ALT_ENTER_MESSAGE;
63our $STATUS_LINE; 100our $STATUSBOX;
64our $DEBUG_STATUS; 101our $DEBUG_STATUS;
65 102
103our $INVWIN;
104our $INV;
105our $INVR;
106our $INVR_LBL;
107our $OPENCONT;
108
66sub status { 109sub status {
67 $STATUS_LINE->set_text ($_[0]); 110 $STATUSBOX->add ($_[0], pri => -10, group => "status", timeout => 20, fg => [1, 1, 0, 1]);
68 $STATUS_LINE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h} - $STATUS_LINE->{h});
69} 111}
70 112
71sub debug { 113sub debug {
72 $DEBUG_STATUS->set_text ($_[0]); 114 $DEBUG_STATUS->set_text ($_[0]);
73 $DEBUG_STATUS->move ($WIDTH - $DEBUG_STATUS->{w}, 0, $DEBUG_STATUS->{w}, $DEBUG_STATUS->{h}); 115 my ($w, $h) = $DEBUG_STATUS->size_request;
116 $DEBUG_STATUS->move ($WIDTH - $w, 0);
74} 117}
75 118
76sub start_game { 119sub start_game {
77 status "logging in..."; 120 status "logging in...";
78 121
79 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32; 122 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
80 123
81 $MAPCACHE = CFClient::db_table "mapcache_$CFG->{host}"; 124 $MAPCACHE = CFClient::db_table "mapcache_$CFG->{host}";
82
83 $MAP = new CFClient::Map $mapsize, $mapsize; 125 $MAP = new CFClient::Map $mapsize, $mapsize;
84 126
85 my ($host, $port) = split /:/, $CFG->{host}; 127 my ($host, $port) = split /:/, $CFG->{host};
86 128
87 $CONN = new conn 129 $CONN = eval {
130 new conn
88 host => $host, 131 host => $host,
89 port => $port || 13327, 132 port => $port || 13327,
90 user => $CFG->{user}, 133 user => $CFG->{user},
91 pass => $CFG->{password}, 134 pass => $CFG->{password},
92 mapw => $mapsize, 135 mapw => $mapsize,
93 maph => $mapsize, 136 maph => $mapsize,
137 ;
94 ; 138 };
95 139
140 if ($CONN) {
141 $LOGIN_BUTTON->set_text ("Logout");
142
96 status "login successful"; 143 status "login successful";
97 144
98 CFClient::lowdelay fileno $CONN->{fh}; 145 CFClient::lowdelay fileno $CONN->{fh};
146 } else {
147 status "unable to connect";
148 stop_game();
149 }
99} 150}
100 151
101sub stop_game { 152sub stop_game {
153 return unless $CONN;
154
155 status "connection closed";
156 $LOGIN_BUTTON->set_text ("Login");
157 $CONN->destroy;
158 $CONN = 0; # false, does not autovivify
159
160 undef $MAPCACHE;
102 undef $CONN; 161 undef $MAP;
103} 162}
104 163
105sub client_setup { 164sub client_setup {
106 my $dialog = new CFClient::UI::FancyFrame 165 my $dialog = new CFClient::UI::FancyFrame
107 title => "Client Setup", 166 title => "Client Setup",
109 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]); 168 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
110 169
111 $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode"); 170 $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode");
112 $table->add (1, 0, my $hbox = new CFClient::UI::HBox); 171 $table->add (1, 0, my $hbox = new CFClient::UI::HBox);
113 172
114 $hbox->add (my $mode_slider = new CFClient::UI::Slider expand => 1, req_w => 100, range => [$CFG->{sdl_mode}, 0, scalar @SDL_MODES, 1]); 173 $hbox->add (my $mode_slider = new CFClient::UI::Slider expand => 1, req_w => 100, range => [$CFG->{sdl_mode}, 0, $#SDL_MODES, 1, 1]);
115 $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999"); 174 $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999");
116 175
117 $mode_slider->connect (changed => sub { 176 $mode_slider->connect (changed => sub {
118 my ($self, $value) = @_; 177 my ($self, $value) = @_;
119 178
139 state => $CFG->{fast}, 198 state => $CFG->{fast},
140 tooltip => "Lower the visual quality considerably to speed up rendering.", 199 tooltip => "Lower the visual quality considerably to speed up rendering.",
141 connect_changed => sub { 200 connect_changed => sub {
142 my ($self, $value) = @_; 201 my ($self, $value) = @_;
143 $CFG->{fast} = $value; 202 $CFG->{fast} = $value;
203 }
204 );
205
206 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Map Scale");
207 $table->add (1, $row++, new CFClient::UI::Slider
208 range => [$CFG->{map_scale}, 0.25, 2, 0.05, 0.05],
209 tooltip => "Enlarge or shrink the displayed map",
210 connect_changed => sub {
211 my ($self, $value) = @_;
212 $CFG->{map_scale} = $value;
144 } 213 }
145 ); 214 );
146 215
147 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War"); 216 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War");
148 $table->add (1, $row++, new CFClient::UI::CheckBox 217 $table->add (1, $row++, new CFClient::UI::CheckBox
175 } 244 }
176 ); 245 );
177 246
178 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize"); 247 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
179 $table->add (1, $row++, new CFClient::UI::Slider 248 $table->add (1, $row++, new CFClient::UI::Slider
180 range => [$CFG->{gui_fontsize}, 0.5, 2, 0.1], 249 range => [$CFG->{gui_fontsize}, 0.5, 2, 0.1, 0.1],
181 tooltip => "The font size used by most GUI elements", 250 tooltip => "The font size used by most GUI elements",
182 connect_changed => sub { 251 connect_changed => sub { $CFG->{gui_fontsize} = $_[1] },
183 $CFG->{gui_fontsize} = 0.1 * int $_[1] * 10;
184# $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
185 }
186 ); 252 );
187 253
188 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Server Log Fontsize"); 254 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Server Log Fontsize");
189 $table->add (1, $row++, new CFClient::UI::Slider 255 $table->add (1, $row++, new CFClient::UI::Slider
190 range => [$CFG->{log_fontsize}, 0.5, 2, 0.1], 256 range => [$CFG->{log_fontsize}, 0.5, 2, 0.1, 0.1],
191 tooltip => "The font size used by the server log window only", 257 tooltip => "The font size used by the server log window only",
192 connect_changed => sub { 258 connect_changed => sub { $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = $_[1]) },
193 $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = 0.1 * int $_[1] * 10);
194 }
195 ); 259 );
196 260
197 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Stats Fontsize"); 261 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Stats Fontsize");
198 262
199 $table->add (1, $row++, new CFClient::UI::Slider 263 $table->add (1, $row++, new CFClient::UI::Slider
200 range => [$CFG->{stat_fontsize}, 0.5, 2, 0.1], 264 range => [$CFG->{stat_fontsize}, 0.5, 2, 0.1, 0.1],
201 tooltip => "The font size used by the statistics window only", 265 tooltip => "The font size used by the statistics window only",
202 connect_changed => sub { 266 connect_changed => sub {
203 $CFG->{stat_fontsize} = 0.1 * int $_[1] * 10; 267 $CFG->{stat_fontsize} = $_[1];
204 &set_stats_window_fontsize; 268 &set_stats_window_fontsize;
205 } 269 }
206 ); 270 );
207 271
208 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size"); 272 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size");
209 $table->add (1, $row++, new CFClient::UI::Slider 273 $table->add (1, $row++, new CFClient::UI::Slider
210 range => [$CFG->{gauge_size}, 0.2, 0.8, 0.02], 274 range => [$CFG->{gauge_size}, 0.2, 0.8, 0.02],
211 tooltip => "Adjust the size of the stats gauges at the bottom right", 275 tooltip => "Adjust the size of the stats gauges at the bottom right",
212 connect_changed => sub { 276 connect_changed => sub {
213 $CFG->{gauge_size} = $_[1]; 277 $CFG->{gauge_size} = $_[1];
214 my $h = int $HEIGHT * $CFG->{gauge_size}; 278 $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
215 $GAUGES->{win}->set_size ($WIDTH, $h);
216 $GAUGES->{win}->move (0, $HEIGHT - $h);
217 } 279 }
218 ); 280 );
219 281
220 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize"); 282 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
221 $table->add (1, $row++, new CFClient::UI::Slider 283 $table->add (1, $row++, new CFClient::UI::Slider
222 range => [$CFG->{gauge_fontsize}, 0.5, 2.0, 0.1], 284 range => [$CFG->{gauge_fontsize}, 0.5, 2.0, 0.1, 0.1],
223 tooltip => "Adjusts the fontsize of the gauges at the bottom right", 285 tooltip => "Adjusts the fontsize of the gauges at the bottom right",
224 connect_changed => sub { 286 connect_changed => sub {
225 $CFG->{gauge_fontsize} = 0.1 * int $_[1] * 10; 287 $CFG->{gauge_fontsize} = $_[1];
226 &set_gauge_window_fontsize; 288 &set_gauge_window_fontsize;
227 #$GAUGES->{win}->check_size;
228 #$GAUGES->{win}->update;
229 } 289 }
230 ); 290 );
231 291
232 $table->add (1, $row++, new CFClient::UI::Button 292 $table->add (1, $row++, new CFClient::UI::Button
233 expand => 1, align => 0, text => "Apply", 293 expand => 1, align => 0, text => "Apply",
275 audio_shutdown (); 335 audio_shutdown ();
276 audio_init (); 336 audio_init ();
277 } 337 }
278 ); 338 );
279 339
340 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Communication cmd");
341 $table->add (1, $row++, my $saycmd = new CFClient::UI::Entry
342 text => $CFG->{say_command},
343 tooltip => "This is the command that will be used if you write a line in the message window entry. "
344 ."Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
345 ."But you could also set it to 'tell <playername>' to only chat with that user.",
346 connect_changed => sub {
347 my ($self, $value) = @_;
348 $CFG->{say_command} = $value;
349 }
350 );
351
280 $dialog 352 $dialog
281} 353}
282 354
283sub set_stats_window_fontsize { 355sub set_stats_window_fontsize {
284 for (values %{$STATWIDS}) { 356 for (values %{$STATWIDS}) {
288 360
289sub set_gauge_window_fontsize { 361sub set_gauge_window_fontsize {
290 for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) { 362 for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
291 $_->set_fontsize ($::CFG->{gauge_fontsize}); 363 $_->set_fontsize ($::CFG->{gauge_fontsize});
292 } 364 }
365
366# local $GAUGES->{win}{parent};#d#
367# use PApp::Util; open D, ">:utf8", "d"; print D PApp::Util::dumpval $GAUGES->{win}; close D;
293} 368}
294 369
295sub make_gauge_window { 370sub make_gauge_window {
296 my $gh = int ($HEIGHT * $CFG->{gauge_size}); 371 my $gh = int $HEIGHT * $CFG->{gauge_size};
297# my $gw = int ($WIDTH * $CFG->{gauge_w_size});
298 372
299 my $win = new CFClient::UI::Frame ( 373 my $win = new CFClient::UI::Frame (
300 y => $HEIGHT - $gh, x => 0, req_w => $WIDTH, req_h => $gh 374 req_y => -1,
375 user_w => $WIDTH,
376 user_h => $gh,
301 ); 377 );
378
302 $win->add (my $vb = new CFClient::UI::VBox); 379 $win->add (my $hbox = new CFClient::UI::HBox
303 380 children => [
304 $vb->add (my $hbg = new CFClient::UI::HBox expand => 1); 381 (new CFClient::UI::HBox expand => 1),
382 (new CFClient::UI::VBox children => [
383 (new CFClient::UI::Empty expand => 1),
384 (new CFClient::UI::Frame bg => [0, 0, 0, 0.4], child => ($FLOORBOX = new CFClient::UI::VBox)),
385 ]),
386 (my $vbox = new CFClient::UI::VBox),
387 ],
388 );
305 389
306 390 $vbox->add (new CFClient::UI::HBox
391 expand => 1,
392 children => [
307 $hbg->add (new CFClient::UI::Empty expand => 1); 393 (new CFClient::UI::Empty expand => 1),
308 $hbg->add (my $hb = new CFClient::UI::HBox); 394 (my $hb = new CFClient::UI::HBox),
395 ],
396 );
397
309 $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp', tooltip => "Health points"); 398 $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp',
399 tooltip => "Health points. 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.");
310 $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana', tooltip => "Spellpoints"); 400 $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana',
401 tooltip => "Spell points. 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.");
311 $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace', tooltip => "Grace"); 402 $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace',
403 tooltip => "Grace points - 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.");
312 $hb->add (my $fg = new CFClient::UI::Gauge type => 'food', tooltip => "Food"); 404 $hb->add (my $fg = new CFClient::UI::Gauge type => 'food',
405 tooltip => "Food. 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.");
313 406
314 $vb->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, text => "XP: 0 LVL: 0"); 407 $vbox->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
408 tooltip => "Experience points and overall level - 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.");
315 $vb->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, text => "Rng:"); 409 $vbox->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
410 tooltip => "Ranged attack - how you attack when you press shift-cursor (spell, skill, weapon etc.)");
316 411
317 $GAUGES = { 412 $GAUGES = {
318 exp => $exp, win => $win, range => $rng, 413 exp => $exp, win => $win, range => $rng,
319 food => $fg, mana => $mg, hp => $hg, grace => $gg 414 food => $fg, mana => $mg, hp => $hg, grace => $gg
320 }; 415 };
416
417 &set_gauge_window_fontsize;
418
321 $win 419 $win
322} 420}
323 421
324sub make_stats_window { 422sub make_stats_window {
325 my $tgw = new CFClient::UI::FancyFrame (x => $WIDTH * 2/5, y => 0, title => "Stats"); 423 my $tgw = new CFClient::UI::FancyFrame x => $WIDTH * 2/5, y => 0, title => "Stats";
326 424
327 $tgw->add (my $vb = new CFClient::UI::VBox); 425 $tgw->add (new CFClient::UI::Window child => my $vb = new CFClient::UI::VBox);
328 $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1); 426 $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1);
329 $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1); 427 $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1);
330 428
331 $vb->add (my $hb = new CFClient::UI::HBox expand => 1); 429 $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
332 430
333 $hb->add (my $tbl = new CFClient::UI::Table expand => 1); 431 $hb->add (my $tbl = new CFClient::UI::Table expand => 1);
334 432
335 $tbl->add (0, 0, $STATWIDS->{st_str} = new CFClient::UI::Label valign => 0, align => +1, template => "30"); 433 my $black = [0, 0, 0];
336 $tbl->add (0, 1, $STATWIDS->{st_dex} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
337 $tbl->add (0, 2, $STATWIDS->{st_con} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
338 $tbl->add (0, 3, $STATWIDS->{st_int} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
339 $tbl->add (0, 4, $STATWIDS->{st_wis} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
340 $tbl->add (0, 5, $STATWIDS->{st_pow} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
341 $tbl->add (0, 6, $STATWIDS->{st_cha} = new CFClient::UI::Label valign => 0, align => +1, template => "30");
342 434
343 $tbl->add (1, 0, $STATWIDS->{st_str_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Str"); 435 for (
344 $tbl->add (1, 1, $STATWIDS->{st_dex_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Dex"); 436 [0, 0, st_str => "Str", 30, "Physical Strength, determines damage dealt with weapons, how much you can carry, and how often you can attack"],
345 $tbl->add (1, 2, $STATWIDS->{st_con_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Con"); 437 [0, 1, st_dex => "Dex", 30, "Dexterity, your physical agility. Determines chance of being hit and affects armor class and speed"],
346 $tbl->add (1, 3, $STATWIDS->{st_int_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Int"); 438 [0, 2, st_con => "Con", 30, "Constitution, physical health and toughness. Determines how many healthpoints you can have"],
347 $tbl->add (1, 4, $STATWIDS->{st_wis_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Wis"); 439 [0, 3, st_int => "Int", 30, "Intelligence, your ability to learn and use skills and incantations (both prayers and magic) and determines how much spell points you can have"],
348 $tbl->add (1, 5, $STATWIDS->{st_pow_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Pow"); 440 [0, 4, st_wis => "Wis", 30, "Wisdom, the ability to learn and use divine magic (prayers). Determines how many grace points you can have"],
349 $tbl->add (1, 6, $STATWIDS->{st_cha_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Cha"); 441 [0, 5, st_pow => "Pow", 30, "Power, your magical potential. Influences the strength of spell effects, and also how much your spell and grace points increase when leveling up"],
442 [0, 6, st_cha => "Cha", 30, "Charisma, how well you are received by NPCs. Affects buying and selling prices in shops."],
350 443
351 $tbl->add (2, 0, $STATWIDS->{st_wc} = new CFClient::UI::Label valign => 0, align => +1, template => "-120"); 444 [2, 0, st_wc => "Wc", -120, "Weapon Class, 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."],
352 $tbl->add (2, 1, $STATWIDS->{st_ac} = new CFClient::UI::Label valign => 0, align => +1, template => "-120"); 445 [2, 1, st_ac => "Ac", -120, "Armour Class, 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."],
353 $tbl->add (2, 2, $STATWIDS->{st_dam} = new CFClient::UI::Label valign => 0, align => +1, template => "120"); 446 [2, 2, st_dam => "Dam", 120, "Damage, how much damage your melee/missile attack inflicts. Higher values indicate a greater amount of damage will be inflicted with each attack."],
354 $tbl->add (2, 3, $STATWIDS->{st_arm} = new CFClient::UI::Label valign => 0, align => +1, template => "120"); 447 [2, 3, st_arm => "Arm", 120, "Armour, 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."],
355 $tbl->add (2, 4, $STATWIDS->{st_spd} = new CFClient::UI::Label valign => 0, align => +1, template => "10.54"); 448 [2, 4, st_spd => "Spd", 10.54, "Speed, 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."],
356 $tbl->add (2, 5, $STATWIDS->{st_wspd} = new CFClient::UI::Label valign => 0, align => +1, template => "9"); 449 [2, 5, st_wspd => "WSp", 10.54, "Weapon Speed, 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."],
450 ) {
451 my ($col, $row, $id, $label, $template, $tooltip) = @$_;
357 452
358 $tbl->add (3, 0, $STATWIDS->{st_wc_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Wc"); 453 $tbl->add ($col , $row, $STATWIDS->{$id} = new CFClient::UI::Label
359 $tbl->add (3, 1, $STATWIDS->{st_ac_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Ac"); 454 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0, align => +1, template => $template, tooltip => $tooltip);
360 $tbl->add (3, 2, $STATWIDS->{st_dam_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Dam"); 455 $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFClient::UI::Label
361 $tbl->add (3, 3, $STATWIDS->{st_arm_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Arm"); 456 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $black, valign => 0, align => -1, text => $label, tooltip => $tooltip);
362 $tbl->add (3, 4, $STATWIDS->{st_spd_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "Sp"); 457 }
363 $tbl->add (3, 5, $STATWIDS->{st_wspd_lbl} = new CFClient::UI::Label valign => 0, align => -1, text => "WSp");
364 458
365 $hb->add (my $tbl2 = new CFClient::UI::Table expand => 1); 459 $hb->add (my $tbl2 = new CFClient::UI::Table expand => 1);
366 460
367 my $row = 0; 461 my $row = 0;
368 my $col = 0; 462 my $col = 0;
369 463
370 my %resist_names = ( 464 my %resist_names = (
371 slow => "Slow", 465 slow => "Slow (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.)",
372 holyw => "Holy Word", 466 holyw => "Holy Word (resistance you against getting the fear when someone whose god doesn't like you spells the holy word on you.)",
373 conf => "Confusion", 467 conf => "Confusion (If you are hit by confusion you will move into random directions, and likely into monsters.)",
374 fire => "Fire", 468 fire => "Fire (just your resistance to fire spells like burning hands, dragonbreath, meteor swarm fire, ...)",
375 depl => "Depletion", 469 depl => "Depletion (some monsters and other effects can cause stats depletion)",
376 magic => "Magic", 470 magic => "Magic (resistance to magic spells like magic missile or similar)",
377 drain => "Draining", 471 drain => "Draining (some monsters (e.g. vampires) and other effects can steal experience)",
378 acid => "Acid", 472 acid => "Acid (resistance to acid, acid hurts pretty much and also corrodes your weapons)",
379 pois => "Poison", 473 pois => "Poison (resistance to getting poisoned)",
380 para => "Paralysation", 474 para => "Paralysation (this resistance affects the chance you get paralysed)",
381 deat => "Death", 475 deat => "Death (resistance against death spells)",
382 phys => "Physical", 476 phys => "Physical (this is the resistance against physical attacks, like when a monster hit you in melee combat)",
383 blind => "Blind", 477 blind => "Blind (blind resistance affects the chance of a successful blinding attack)",
384 fear => "Fear", 478 fear => "Fear (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)",
385 tund => "Turn undead", 479 tund => "Turn undead",
386 elec => "Electricity", 480 elec => "Electricity (resistance againt electricity, spells like large lightning, small lightning, ...)",
387 cold => "Cold", 481 cold => "Cold (this is your resistance against cold spells like icestorm, snowstorm, ...)",
388 ghit => "Ghost hit", 482 ghit => "Ghost hit (special attack used by ghosts and ghost-like beings)",
389 ); 483 );
390 for (qw/slow holyw conf fire depl magic 484 for (qw/slow holyw conf fire depl magic
391 drain acid pois para deat phys 485 drain acid pois para deat phys
392 blind fear tund elec cold ghit/) 486 blind fear tund elec cold ghit/)
393 { 487 {
394 $tbl2->add ($col, $row, 488 $tbl2->add ($col, $row,
395 $STATWIDS->{"res_$_"} = 489 $STATWIDS->{"res_$_"} =
396 new CFClient::UI::Label 490 new CFClient::UI::Label
491 font => $FONT_FIXED,
397 template => "-100%", 492 template => "-100%",
398 align => +1, 493 align => +1,
399 valign => 0, 494 valign => 0,
495 can_events => 1,
496 can_hover => 1,
400 tooltip => $resist_names{$_} 497 tooltip => $resist_names{$_},
401 ); 498 );
402 $tbl2->add ($col + 1, $row, new CFClient::UI::Image 499 $tbl2->add ($col + 1, $row, new CFClient::UI::Image
500 font => $FONT_FIXED,
403 can_hover => 1, 501 can_hover => 1,
404 can_events => 1, 502 can_events => 1,
405 image => "ui/resist/resist_$_.png", 503 image => "ui/resist/resist_$_.png",
406 tooltip => $resist_names{$_} 504 tooltip => $resist_names{$_},
407 ); 505 );
408 506
409 $row++; 507 $row++;
410 if ($row % 6 == 0) { 508 if ($row % 6 == 0) {
411 $col += 2; 509 $col += 2;
417 update_stats_window ({}); 515 update_stats_window ({});
418 516
419 $tgw 517 $tgw
420} 518}
421 519
520sub formsep {
521 reverse join ",", grep length, split /(...)/, reverse $_[0] * 1
522}
523
422sub update_stats_window { 524sub update_stats_window {
423 my ($stats) = @_; 525 my ($stats) = @_;
424 526
425 # i love text protocols!!! 527 # i love text protocols!!!
426 my $hp = $stats->{1} * 1; 528 my $hp = $stats->{Crossfire::Protocol::CS_STAT_HP} * 1;
427 my $hp_m = $stats->{2} * 1; 529 my $hp_m = $stats->{Crossfire::Protocol::CS_STAT_MAXHP} * 1;
428 my $sp = $stats->{3} * 1; 530 my $sp = $stats->{Crossfire::Protocol::CS_STAT_SP} * 1;
429 my $sp_m = $stats->{4} * 1; 531 my $sp_m = $stats->{Crossfire::Protocol::CS_STAT_MAXSP} * 1;
430 my $fo = $stats->{18} * 1; 532 my $fo = $stats->{Crossfire::Protocol::CS_STAT_FOOD} * 1;
431 my $fo_m = 999; 533 my $fo_m = 999;
432 my $gr = $stats->{23} * 1; 534 my $gr = $stats->{Crossfire::Protocol::CS_STAT_GRACE} * 1;
433 my $gr_m = $stats->{24} * 1; 535 my $gr_m = $stats->{Crossfire::Protocol::CS_STAT_MAXGRACE} * 1;
434 536
435 $GAUGES->{hp} ->set_value ($hp, $hp_m); 537 $GAUGES->{hp} ->set_value ($hp, $hp_m);
436 $GAUGES->{mana} ->set_value ($sp, $sp_m); 538 $GAUGES->{mana} ->set_value ($sp, $sp_m);
437 $GAUGES->{food} ->set_value ($fo, $fo_m); 539 $GAUGES->{food} ->set_value ($fo, $fo_m);
438 $GAUGES->{grace} ->set_value ($gr, $gr_m); 540 $GAUGES->{grace} ->set_value ($gr, $gr_m);
439 $GAUGES->{exp} ->set_text ("XP: " . ($stats->{11} || $stats->{28}) * 1 541 $GAUGES->{exp} ->set_text ("Exp: " . (formsep $stats->{Crossfire::Protocol::CS_STAT_EXP64})
440 ." LVL: " . $stats->{12} * 1); 542 . " (lvl " . ($stats->{Crossfire::Protocol::CS_STAT_LEVEL} * 1) . ")");
441 my $rng = $stats->{20}; 543 my $rng = $stats->{Crossfire::Protocol::CS_STAT_RANGE};
442 $rng =~ s/^Range: //; # thank you so much dear server 544 $rng =~ s/^Range: //; # thank you so much dear server
443 $GAUGES->{range} ->set_text ("Rng: " . $rng); 545 $GAUGES->{range} ->set_text ("Rng: " . $rng);
444 my $title = $stats->{21}; 546 my $title = $stats->{Crossfire::Protocol::CS_STAT_TITLE};
445 $title =~ s/^Player: //; 547 $title =~ s/^Player: //;
446 $STATWIDS->{title} ->set_text ("Title: " . $title); 548 $STATWIDS->{title} ->set_text ("Title: " . $title);
447 549
448 $STATWIDS->{st_str} ->set_text (sprintf "%d", $stats->{5}); 550 $STATWIDS->{st_str} ->set_text (sprintf "%d", $stats->{5});
449 $STATWIDS->{st_dex} ->set_text (sprintf "%d", $stats->{8}); 551 $STATWIDS->{st_dex} ->set_text (sprintf "%d", $stats->{8});
450 $STATWIDS->{st_con} ->set_text (sprintf "%d", $stats->{9}); 552 $STATWIDS->{st_con} ->set_text (sprintf "%d", $stats->{9});
451 $STATWIDS->{st_int} ->set_text (sprintf "%d", $stats->{6}); 553 $STATWIDS->{st_int} ->set_text (sprintf "%d", $stats->{6});
452 $STATWIDS->{st_wis} ->set_text (sprintf "%d", $stats->{7}); 554 $STATWIDS->{st_wis} ->set_text (sprintf "%d", $stats->{7});
453 $STATWIDS->{st_pow} ->set_text (sprintf "%d", $stats->{22}); 555 $STATWIDS->{st_pow} ->set_text (sprintf "%d", $stats->{22});
454 $STATWIDS->{st_cha} ->set_text (sprintf "%d", $stats->{10}); 556 $STATWIDS->{st_cha} ->set_text (sprintf "%d", $stats->{10});
455 $STATWIDS->{st_wc} ->set_text (sprintf "%d", $stats->{13}); 557 $STATWIDS->{st_wc} ->set_text (sprintf "%d", $stats->{13});
456 $STATWIDS->{st_ac} ->set_text (sprintf "%d", $stats->{14}); 558 $STATWIDS->{st_ac} ->set_text (sprintf "%d", $stats->{14});
457 $STATWIDS->{st_dam} ->set_text (sprintf "%d", $stats->{15}); 559 $STATWIDS->{st_dam} ->set_text (sprintf "%d", $stats->{15});
458 $STATWIDS->{st_arm} ->set_text (sprintf "%d", $stats->{16}); 560 $STATWIDS->{st_arm} ->set_text (sprintf "%d", $stats->{16});
459 $STATWIDS->{st_spd} ->set_text (sprintf "%.1f", $stats->{17}); 561 $STATWIDS->{st_spd} ->set_text (sprintf "%.1f", $stats->{Crossfire::Protocol::CS_STAT_SPEED});
460 $STATWIDS->{st_wspd}->set_text (sprintf "%.1f", $stats->{19}); 562 $STATWIDS->{st_wspd}->set_text (sprintf "%.1f", $stats->{Crossfire::Protocol::CS_STAT_WEAP_SP});
461 563
462 my %tbl = ( 564 my %tbl = (
463 phys => 100, 565 phys => 100,
464 magic => 101, 566 magic => 101,
465 fire => 102, 567 fire => 102,
486 588
487} 589}
488 590
489sub metaserver_dialog { 591sub metaserver_dialog {
490 my $dialog = new CFClient::UI::FancyFrame 592 my $dialog = new CFClient::UI::FancyFrame
491 title => "Metaserver", 593 title => "Server List",
492 child => (my $vbox = new CFClient::UI::VBox); 594 child => (my $vbox = new CFClient::UI::VBox);
493 595
494 $vbox->add ($dialog->{table} = new CFClient::UI::Table); 596 $vbox->add ($dialog->{table} = new CFClient::UI::Table);
495 597
496 $dialog 598 $dialog
497} 599}
600
601my $METASERVER_ATIME;
498 602
499sub update_metaserver { 603sub update_metaserver {
500 my ($HOST) = @_; 604 my ($HOST) = @_;
501 605
502 status "fetching metaserver list..."; 606 return if $METASERVER_ATIME > time;
607 $METASERVER_ATIME = time + 60;
608
609 my $table = $METASERVER->{table};
610 $table->clear;
611 $table->add (0, 0, my $label = new CFClient::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
503 612
504 my $buf; 613 my $buf;
505 614
506 my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0; 615 my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0;
616
617 unless ($fh) {
618 $label->set_text ("unable to contact metaserver: $!");
619 return;
620 }
507 621
508 Event->io (fd => $fh, poll => 'r', cb => sub { 622 Event->io (fd => $fh, poll => 'r', cb => sub {
509 my $res = sysread $fh, $buf, 8192, length $buf; 623 my $res = sysread $fh, $buf, 8192, length $buf;
510 624
511 if (!defined $res) { 625 if (!defined $res) {
512 $_[0]->w->cancel; 626 $_[0]->w->cancel;
513 status "metaserver: $!"; 627 $label->set_text ("error while retrieving server list: $!");
514 } elsif ($res == 0) { 628 } elsif ($res == 0) {
515 $_[0]->w->cancel; 629 $_[0]->w->cancel;
516 status "server list retrieved"; 630 status "server list retrieved";
517 631
518 my $table = $METASERVER->{table}; 632 utf8::decode $buf if utf8::valid $buf;
519 633
520 $table->clear; 634 $table->clear;
521 635
522 my @col = qw(Use #Users Host Uptime Version Description); 636 my @col = qw(Use #Users Host Uptime Version Description);
523 $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_]) 637 $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_])
547 $m = [$users, $host, $uptime, $version, $desc]; 661 $m = [$users, $host, $uptime, $version, $desc];
548 662
549 $y++; 663 $y++;
550 664
551 $table->add (0, $y, new CFClient::UI::VBox children => [ 665 $table->add (0, $y, new CFClient::UI::VBox children => [
552 (new CFClient::UI::Button text => " ", connect_activate => sub { 666 (new CFClient::UI::Button text => "Use", connect_activate => sub {
553 $HOST->set_text ($CFG->{host} = $host); 667 $HOST->set_text ($CFG->{host} = $host);
554 }), 668 }),
555 (new CFClient::UI::Empty expand => 1), 669 (new CFClient::UI::Empty expand => 1),
556 ]); 670 ]);
557 671
558 $table->add ($_ + 1, $y, new CFClient::UI::Label align => $align[$_], text => $m->[$_], fontsize => 0.8) 672 $table->add ($_ + 1, $y, new CFClient::UI::Label
673 ellipsise => 0, align => $align[$_], text => $m->[$_], fontsize => 0.8)
559 for 0 .. $#$m; 674 for 0 .. $#$m;
560 } 675 }
561 } 676 }
562 }); 677 });
563} 678}
586 701
587 $METASERVER = metaserver_dialog; 702 $METASERVER = metaserver_dialog;
588 703
589 $vbox->add (new CFClient::UI::Flopper 704 $vbox->add (new CFClient::UI::Flopper
590 expand => 1, 705 expand => 1,
591 text => "Metaserver", 706 text => "Server List",
592 other => $METASERVER, 707 other => $METASERVER,
593 tooltip => "Show a list of avaible crossfire servers", 708 tooltip => "Show a list of available crossfire servers",
594 connect_open => sub { 709 connect_open => sub {
595 update_metaserver $HOST; 710 update_metaserver $HOST;
596 } 711 }
597 ); 712 );
598 } 713 }
616 my ($self, $value) = @_; 731 my ($self, $value) = @_;
617 $CFG->{password} = $value; 732 $CFG->{password} = $value;
618 } 733 }
619 ); 734 );
620 735
621 $table->add (0, 6, new CFClient::UI::Label valign => 0, align => 1, text => "Def. say cmd");
622 $table->add (1, 6, my $saycmd = new CFClient::UI::Entry
623 text => $CFG->{say_command},
624 tooltip => "This is the command that will be used if you write a line in the message window entry. "
625 ."Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
626 ."But you could also set it to 'tell <playername>' to only chat with that user.",
627 connect_changed => sub {
628 my ($self, $value) = @_;
629 $CFG->{say_command} = $value;
630 }
631 );
632
633 $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size"); 736 $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size");
634 $table->add (1, 7, new CFClient::UI::Slider 737 $table->add (1, 7, new CFClient::UI::Slider
635 req_w => 100, 738 req_w => 100,
636 range => [$CFG->{mapsize}, 10, 100 + 1, 1], 739 range => [$CFG->{mapsize}, 10, 100 + 1, 1, 1],
637 tooltip => "This is the size of the portion of the map update the server sends you. " 740 tooltip => "This is the size of the portion of the map update the server sends you. "
638 ."If you set this to a high value you will be able to see further for example.", 741 ."If you set this to a high value you will be able to see further for example.",
639 connect_changed => sub { 742 connect_changed => sub {
640 my ($self, $value) = @_; 743 my ($self, $value) = @_;
641 744
642 $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 745 $CFG->{mapsize} = $self->{range}[0] = $value = int $value;
643 }, 746 },
644 ); 747 );
645 748
646 $table->add (1, 8, new CFClient::UI::Button expand => 1, align => 0, text => "Login", connect_activate => sub { 749 $table->add (1, 8, $LOGIN_BUTTON = new CFClient::UI::Button
750 expand => 1,
751 align => 0,
752 text => "Login",
753 connect_activate => sub {
754 $CONN ? stop_game
647 start_game; 755 : start_game;
756 },
648 }); 757 );
649 758
650 $dialog 759 $dialog
651} 760}
652 761
653sub message_window { 762sub message_window {
654 my $window = new CFClient::UI::FancyFrame 763 my $window = new CFClient::UI::FancyFrame
655 title => "Messages", 764 title => "Messages",
656 border_bg => [1, 1, 1, 0.5], 765 border_bg => [1, 1, 1, 1],
657 bg => [0.3, 0.3, 0.3, 0.8], 766 bg => [0, 0, 0, 0.5],
658 user_w => int $::WIDTH / 3, 767 user_w => int $::WIDTH / 3,
659 user_h => int $::HEIGHT / 5, 768 user_h => int $::HEIGHT / 5,
660 child => (my $vbox = new CFClient::UI::VBox); 769 child => (my $vbox = new CFClient::UI::VBox);
661 770
662 $vbox->add ($LOGVIEW = new CFClient::UI::TextView 771 $vbox->add ($LOGVIEW = new CFClient::UI::TextView
702 }; 811 };
703 812
704 $window 813 $window
705} 814}
706 815
816sub make_inventory_window {
817 my $invwin = new CFClient::UI::FancyFrame
818 user_w => $WIDTH * (4/5), user_h => $HEIGHT * (4/5), title => "Inventory";
819
820 $invwin->add (my $hb = new CFClient::UI::HBox);
821
822 $hb->add (my $vb1 = new CFClient::UI::VBox expand => 1);
823 $vb1->add (my $lbl = new CFClient::UI::Label);
824 $lbl->set_text ("Player");
825 $vb1->add ($INV = new CFClient::UI::Inventory expand => 1);
826
827 $hb->add (my $vb2 = new CFClient::UI::VBox expand => 1);
828 $vb2->add ($INVR_LBL = new CFClient::UI::Label);
829 $INVR_LBL->set_text ("Floor");
830 $vb2->add ($INVR = new CFClient::UI::Inventory expand => 1);
831
832 $invwin
833}
834
707sub sdl_init { 835sub sdl_init {
708 CFClient::SDL_Init 836 CFClient::SDL_Init
709 and die "SDL::Init failed!\n"; 837 and die "SDL::Init failed!\n";
710} 838}
711 839
712sub video_init { 840sub video_init {
713 sdl_init; 841 sdl_init;
714 842
843 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
844
715 ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] }; 845 ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
716 $FULLSCREEN = $CFG->{fullscreen}; 846 $FULLSCREEN = $CFG->{fullscreen};
717 $FAST = $CFG->{fast}; 847 $FAST = $CFG->{fast};
718 848
719 CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN 849 CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
720 or die "SDL_SetVideoMode failed!\n"; 850 or die "SDL_SetVideoMode failed!\n";
721 851
722 $SDL_ACTIVE = 1; 852 $SDL_ACTIVE = 1;
723
724 $LAST_REFRESH = time - 0.01; 853 $LAST_REFRESH = time - 0.01;
725 854
726 CFClient::gl_init; 855 CFClient::gl_init;
727 856
728 $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize}; 857 $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
729 858
859 $CFClient::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
860
730 ############################################################################# 861 #############################################################################
731 862
863 unless ($DEBUG_STATUS) {
864 # create the widgets
865
732 $DEBUG_STATUS = new CFClient::UI::Label padding => 0, z => 100; 866 $DEBUG_STATUS = new CFClient::UI::Label padding => 0, z => 100, req_x => -1;
733 $DEBUG_STATUS->show; 867 $DEBUG_STATUS->show;
734 868
735 $STATUS_LINE = new CFClient::UI::Label 869 $STATUSBOX = new CFClient::UI::Statusbox;
736 padding => 0, 870 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", pri => -100, color => [1, 1, 1, 0.8]);
737 y => $HEIGHT - $FONTSIZE * 1.8;
738 $STATUS_LINE->show;
739 871
740 $ALT_ENTER_MESSAGE = new CFClient::UI::Label 872 (new CFClient::UI::Frame
741 padding => 0, 873 bg => [0, 0, 0, 0.4],
742 fontsize => 0.8, 874 req_y => -1,
743 markup => "Use <b>Alt-Enter</b> to toggle fullscreen mode"; 875 child => $STATUSBOX,
744 $ALT_ENTER_MESSAGE->show; 876 )->show;
745 $ALT_ENTER_MESSAGE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h});
746 877
747 $CFClient::UI::ROOT->add ($MAPWIDGET = new CFClient::MapWidget); 878 CFClient::UI::FancyFrame->new (
748 $MAPWIDGET->focus_in; 879 border_bg => [1, 1, 1, 192/255],
880 bg => [1, 1, 1, 0],
881 child => ($MAPMAP = new CFClient::MapWidget::MapMap),
882 )->show;
883
884 $MAPWIDGET = new CFClient::MapWidget;
749 $MAPWIDGET->connect (activate_console => sub { 885 $MAPWIDGET->connect (activate_console => sub {
750 my ($mapwidget, $preset) = @_; 886 my ($mapwidget, $preset) = @_;
751 887
752 if ($CONSOLE) { 888 if ($CONSOLE) {
753 $CONSOLE->{input}->{auto_activated} = 1; 889 $CONSOLE->{input}->{auto_activated} = 1;
754 $CONSOLE->{input}->focus_in; 890 $CONSOLE->{input}->focus_in;
755 891
756 if ($preset && $CONSOLE->{input}->get_text eq '') { 892 if ($preset && $CONSOLE->{input}->get_text eq '') {
757 $CONSOLE->{input}->set_text ($preset); 893 $CONSOLE->{input}->set_text ($preset);
894 }
758 } 895 }
759 } 896 });
760 }); 897 $MAPWIDGET->show;
898 $MAPWIDGET->focus_in;
761 899
762 $CFClient::UI::ROOT->add ($BUTTONBAR = new CFClient::UI::HBox); 900 $BUTTONBAR = new CFClient::UI::HBox;
763 901
764 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Client Setup", other => client_setup); 902 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Client Setup", other => client_setup);
765 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Server Setup", other => server_setup); 903 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Server Setup", other => server_setup);
766 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window); 904 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window);
767 905
768 $CFClient::UI::ROOT->add (make_gauge_window); # 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 906 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
907
769 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Stats Window", other => make_stats_window); 908 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Stats Window", other => make_stats_window);
909 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Inventory", other => make_inventory_window);
770 910
771 $BUTTONBAR->add (new CFClient::UI::Button text => "Save Config", connect_activate => sub { 911 $BUTTONBAR->add (new CFClient::UI::Button text => "Save Config", connect_activate => sub {
772 CFClient::write_cfg "$Crossfire::VARDIR/pclientrc"; 912 CFClient::write_cfg "$Crossfire::VARDIR/pclientrc";
773 status "Configuration Saved"; 913 status "Configuration Saved";
774 }); 914 });
775 915
916 $BUTTONBAR->show;
917
918 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
919
920 # delay till geometry is constant
921 $CFClient::UI::ROOT->on_post_alloc (startup => sub {
776 $BUTTONBAR->{children}[1]->emit ("activate"); # pop up server setup 922 $BUTTONBAR->{children}[1]->emit ("activate"); # pop up server setup
923 my $widget = $GAUGES->{win};
924 $widget->move (0, $HEIGHT - $widget->{h});#d# to in toplevel
925 });
926 force_refresh ();
927 }
777} 928}
778 929
779sub video_shutdown { 930sub video_shutdown {
780 $CFClient::UI::ROOT->{children} = [];
781 undef $SDL_ACTIVE; 931 undef $SDL_ACTIVE;
782} 932}
783 933
784my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d# 934my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
785my $bgmusic;#TODO#hack#d# 935my $bgmusic;#TODO#hack#d#
936
937sub audio_channel_finished {
938 my ($channel) = @_;
939
940 warn "channel $channel finished\n";#d#
941}
786 942
787sub audio_music_finished { 943sub audio_music_finished {
788 return unless $CFG->{bgm_enable}; 944 return unless $CFG->{bgm_enable};
789 945
790 # TODO: hack, do play loop and mood music 946 # TODO: hack, do play loop and mood music
794 push @bgmusic, shift @bgmusic; 950 push @bgmusic, shift @bgmusic;
795} 951}
796 952
797sub audio_init { 953sub audio_init {
798 if ($CFG->{audio_enable}) { 954 if ($CFG->{audio_enable}) {
799 if (open my $fh, "<:utf8", CFClient::find_rcfile "sounds/config") { 955 if (open my $fh, "<", CFClient::find_rcfile "sounds/config") {
800 $SDL_MIXER = !CFClient::Mix_OpenAudio; 956 $SDL_MIXER = !CFClient::Mix_OpenAudio;
801 CFClient::Mix_AllocateChannels 8; 957 CFClient::Mix_AllocateChannels 8;
802 CFClient::MixMusic::volume $CFG->{bgm_volume} * 128; 958 CFClient::MixMusic::volume $CFG->{bgm_volume} * 128;
803 959
804 audio_music_finished; 960 audio_music_finished;
831} 987}
832 988
833my %animate_object; 989my %animate_object;
834my $animate_timer; 990my $animate_timer;
835 991
836my $want_refresh;
837my $can_refresh;
838
839my $fps = 9; 992my $fps = 9;
840 993
841sub force_refresh { 994sub force_refresh {
842 $fps = $fps * 0.95 + 1 / ($NOW - $LAST_REFRESH) * 0.05; 995 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
843 debug sprintf "%3.2f", $fps; 996 debug sprintf "%3.2f", $fps;
844 997
845 $want_refresh = 0;
846 $can_refresh = 0;
847
848 $CFClient::UI::ROOT->draw; 998 $CFClient::UI::ROOT->draw;
849
850 CFClient::SDL_GL_SwapBuffers; 999 CFClient::SDL_GL_SwapBuffers;
851 1000
1001 $WANT_REFRESH = 0;
1002 $CAN_REFRESH = 0;
852 $LAST_REFRESH = $NOW; 1003 $LAST_REFRESH = $NOW;
853} 1004}
854 1005
855my $refresh_watcher = Event->timer (after => 0, hard => 1, interval => 1 / $MAX_FPS, cb => sub { 1006my $refresh_watcher = Event->timer (after => 0, hard => 1, interval => 1 / $MAX_FPS, cb => sub {
856 $NOW = time; 1007 $NOW = time;
858 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_) 1009 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
859 for CFClient::SDL_PollEvent; 1010 for CFClient::SDL_PollEvent;
860 1011
861 if (%animate_object) { 1012 if (%animate_object) {
862 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object; 1013 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
863 $want_refresh++; 1014 $WANT_REFRESH++;
864 } 1015 }
865 1016
866 if ($want_refresh) { 1017 if ($WANT_REFRESH) {
867 force_refresh; 1018 force_refresh;
868 } else { 1019 } else {
869 $can_refresh = 1; 1020 $CAN_REFRESH = 1;
870 } 1021 }
871}); 1022});
872
873sub refresh {
874 $want_refresh++;
875}
876 1023
877sub animation_start { 1024sub animation_start {
878 my ($widget) = @_; 1025 my ($widget) = @_;
879 $animate_object{$widget} = $widget; 1026 $animate_object{$widget} = $widget;
880} 1027}
886 1033
887@conn::ISA = Crossfire::Protocol::; 1034@conn::ISA = Crossfire::Protocol::;
888 1035
889sub conn::stats_update { 1036sub conn::stats_update {
890 my ($self, $stats) = @_; 1037 my ($self, $stats) = @_;
1038
1039 if (my $exp = $stats->{Crossfire::Protocol::CS_STAT_EXP64}) {
1040 my $diff = $exp - $self->{prev_exp};
1041 $STATUSBOX->add ("$diff experience gained", group => "experience $diff", fg => [0.5, 1, 0.5, 0.8], timeout => 5)
1042 if exists $self->{prev_exp} && $diff;
1043 $self->{prev_exp} = $exp;
1044 }
891 1045
892 update_stats_window ($stats); 1046 update_stats_window ($stats);
893} 1047}
894 1048
895sub conn::user_send { 1049sub conn::user_send {
959# at worst. 1113# at worst.
960sub conn::flood_fill { 1114sub conn::flood_fill {
961 my ($self, $gx, $gy, $path, $hash, $flags) = @_; 1115 my ($self, $gx, $gy, $path, $hash, $flags) = @_;
962 1116
963 # the server does not allow map paths > 6 1117 # the server does not allow map paths > 6
964 return if 6 <= length $path; 1118 return if 7 <= length $path;
965 1119
966 my ($x0, $y0, $x1, $y1) = @{$self->{neigh_rect}}; 1120 my ($x0, $y0, $x1, $y1) = @{$self->{neigh_rect}};
967 1121
968 for ( 1122 for (
969 [1, 0, -1], 1123 [1, 0, -1],
1012 1166
1013 $self->flush_map; 1167 $self->flush_map;
1014 1168
1015 my ($ox, $oy) = ($::MAP->ox, $::MAP->oy); 1169 my ($ox, $oy) = ($::MAP->ox, $::MAP->oy);
1016 1170
1017 my $mapmapw = 250; 1171 my $mapmapw = $MAPMAP->{w};
1018 my $mapmaph = 250; 1172 my $mapmaph = $MAPMAP->{h};
1019 1173
1020 $self->{neigh_rect} = [ 1174 $self->{neigh_rect} = [
1021 $ox - $mapmapw * 0.5, $oy - $mapmapw * 0.5, 1175 $ox - $mapmapw * 0.5, $oy - $mapmapw * 0.5,
1022 $ox + $mapmapw * 0.5 + $w, $oy + $mapmapw * 0.5 + $h, 1176 $ox + $mapmapw * 0.5 + $w, $oy + $mapmapw * 0.5 + $h,
1023 ]; 1177 ];
1066 } 1220 }
1067 1221
1068gotid: 1222gotid:
1069 $face->{id} = $id; 1223 $face->{id} = $id;
1070 $MAP->set_face ($facenum => $id); 1224 $MAP->set_face ($facenum => $id);
1225 $self->{faceid}[$facenum] = $id;#d#
1071 $TILECACHE->get ($id) 1226 $TILECACHE->get ($id)
1072} 1227}
1073 1228
1074sub conn::face_update { 1229sub conn::face_update {
1075 my ($self, $facenum, $face) = @_; 1230 my ($self, $facenum, $face) = @_;
1083 my ($self, $id, $data) = @_; 1238 my ($self, $id, $data) = @_;
1084 1239
1085 $self->{texture}[$id] ||= do { 1240 $self->{texture}[$id] ||= do {
1086 my $tex = 1241 my $tex =
1087 new_from_image CFClient::Texture 1242 new_from_image CFClient::Texture
1088 $data, minify => 1; 1243 $data, minify => 1, mipmap => 1;
1089 1244
1090 $MAP->set_texture ($id, @$tex{qw(name w h s t)}, @{$tex->{minified}}); 1245 $MAP->set_texture ($id, @$tex{qw(name w h s t)}, @{$tex->{minified}});
1091 $MAPWIDGET->update; 1246 $MAPWIDGET->update;
1092 1247
1093 $tex 1248 $tex
1105 1260
1106 $chunk->play; 1261 $chunk->play;
1107# warn "sound $x,$y,$soundnum,$type\n";#d# 1262# warn "sound $x,$y,$soundnum,$type\n";#d#
1108} 1263}
1109 1264
1265my $LAST_QUERY; # server is stupid, stupid, stupid
1266
1110sub conn::query { 1267sub conn::query {
1111 my ($self, $flags, $prompt) = @_; 1268 my ($self, $flags, $prompt) = @_;
1112 1269
1113 #TODO, display dialog with relevant information 1270 $prompt = $LAST_QUERY unless length $prompt;
1114 warn "<<<<QUERY:$flags:$prompt>>>\n";#d# 1271 $LAST_QUERY = $prompt;
1272
1273 my $dialog = new CFClient::UI::FancyFrame
1274 title => "Query",
1275 child => my $vbox = new CFClient::UI::VBox;
1276
1277 $vbox->add (new CFClient::UI::Label
1278 max_w => $::WIDTH * 0.4,
1279 text => $prompt);
1280
1281 if ($flags & Crossfire::Protocol::CS_QUERY_YESNO) {
1282 $vbox->add (my $hbox = new CFClient::HBox);
1283 $hbox->add (new CFClient::Button
1284 text => "No",
1285 connect_activate => sub {
1286 $self->send ("reply n");
1287 $dialog->destroy;
1288 $MAPWIDGET->focus_in;
1289 }
1290 );
1291 $hbox->add (new CFClient::Button
1292 text => "Yes",
1293 connect_activate => sub {
1294 $self->send ("reply y");
1295 $dialog->destroy;
1296 $MAPWIDGET->focus_in;
1297 },
1298 );
1299
1300 $dialog->focus_in;
1301
1302 } elsif ($flags & Crossfire::Protocol::CS_QUERY_SINGLECHAR) {
1303 $dialog->{tooltip} = "Press a key (click on the entry to make sure it has keyboard focus)";
1304 $vbox->add (my $entry = new CFClient::UI::Entry
1305 connect_changed => sub {
1306 $self->send ("reply $_[1]");
1307 $dialog->destroy;
1308 $MAPWIDGET->focus_in;
1309 },
1310 );
1311
1312 $entry->focus_in;
1313
1314 } else {
1315 $dialog->{tooltip} = "Enter the reply and press return (click on the entry to make sure it has keyboard focus)";
1316
1317 $vbox->add (my $entry = new CFClient::UI::Entry
1318 $flags & Crossfire::Protocol::CS_QUERY_HIDEINPUT ? (hiddenchar => "*") : (),
1319 connect_activate => sub {
1320 $self->send ("reply $_[1]");
1321 $dialog->destroy;
1322 $MAPWIDGET->focus_in;
1323 },
1324 );
1325
1326 $entry->focus_in;
1327 }
1328
1329 $dialog->show;
1115} 1330}
1116 1331
1117sub conn::drawinfo { 1332sub conn::drawinfo {
1118 my ($self, $color, $text) = @_; 1333 my ($self, $color, $text) = @_;
1119 1334
1131 [0.55, 0.41, 0.13], 1346 [0.55, 0.41, 0.13],
1132 [0.99, 0.77, 0.26], 1347 [0.99, 0.77, 0.26],
1133 [0.74, 0.65, 0.41], 1348 [0.74, 0.65, 0.41],
1134 ); 1349 );
1135 1350
1351 my $time = sprintf "%02d:%02d:%02d", (localtime time)[2,1,0];
1352
1353 $text = CFClient::UI::Label::escape $text;
1354 $text =~ s/\[b\](.*?)\[\/b\]/<b>\1<\/b>/g;
1355 $text =~ s/\[color=(.*?)\](.*?)\[\/color\]/<span foreground='\1'>\2<\/span>/g;
1356
1136 $LOGVIEW->add_paragraph ($color[$color], $text); 1357 $LOGVIEW->add_paragraph ($color[$color],
1358 join "\n", map "$time $_", split /\n/, $text);
1359
1360 $STATUSBOX->add ($text,
1361 group => $text,
1362 fg => $color[$color],
1363 timeout => 60,
1364 tooltip_font => $::FONT_FIXED,
1365 );
1366}
1367
1368sub conn::drawextinfo {
1369 my ($self, $color, $type, $subtype, $message) = @_;
1370
1371 $self->drawinfo ($color, $message);
1137} 1372}
1138 1373
1139sub conn::spell_add { 1374sub conn::spell_add {
1140 my ($self, $spell) = @_; 1375 my ($self, $spell) = @_;
1141 1376
1377 # TODO
1378 # create a widget dynamically, using spell face (CF::Protocol downloads them)
1142 $MAPWIDGET->add_command ("invoke $spell->{name}", $spell->{message}, sub { 1379 $MAPWIDGET->add_command ("invoke $spell->{name}", CFClient::UI::Label::escape $spell->{message});
1143 });
1144 $MAPWIDGET->add_command ("cast $spell->{name}", $spell->{message}, sub { 1380 $MAPWIDGET->add_command ("cast $spell->{name}", CFClient::UI::Label::escape $spell->{message});
1145 });
1146} 1381}
1147 1382
1148sub conn::spell_delete { 1383sub conn::spell_delete {
1149 my ($self, $spell) = @_; 1384 my ($self, $spell) = @_;
1150} 1385}
1151 1386
1152sub conn::addme_success { 1387sub conn::addme_success {
1153 my ($self) = @_; 1388 my ($self) = @_;
1154 1389
1390 $MAPWIDGET->clr_commands;
1391
1155 for my $skill (values %{$self->{skill_info}}) { 1392 for my $skill (values %{$self->{skill_info}}) {
1156 $MAPWIDGET->add_command ("ready_skill $skill", "", sub { 1393 $MAPWIDGET->add_command ("ready_skill $skill", CFClient::UI::Label::escape "Ready the skill '$skill'");
1394 $MAPWIDGET->add_command ("use_skill $skill", CFClient::UI::Label::escape "Immediately use the skill '$skill'");
1395 }
1396
1397 $MAPWIDGET->add_command ("petmode defend", "Tell pets to stay close to you and defend you");
1398 $MAPWIDGET->add_command ("petmode arena", "Same as petmode sad, but also attack other players");
1399 $MAPWIDGET->add_command ("petmode sad", "Search &amp; Destroy - tell pets to roam about and attack enemies");
1400 $MAPWIDGET->add_command ("killpets", "Kill your pets");
1401 $MAPWIDGET->add_command ("chat", "chat TEXT\nChat with all other players");
1402 $MAPWIDGET->add_command ("shout", "shout TEXT\nShout loudly, used for emergencies");
1403 $MAPWIDGET->add_command ("tell", "tell USERNAME TEXT\nPrivately tell a specific player");
1404
1405 my $parser = new Pod::POM;
1406 my $pod = $parser->parse_file (CFClient::find_rcfile "pod/command_help.pod");
1407
1408 for my $head2 ($pod->head2) {
1409 $head2->title =~ /^(\S+) (?:\s+ \( ([^\)]*) \) )?/x
1410 or next;
1411
1412 my $cmd = $1;
1413 my @args = split /\|/, $2;
1414 @args = (".*") unless @args;
1415
1416 my $text = CFClient::pod_to_pango $head2;
1417
1418 for my $arg (@args) {
1419 $arg = $arg eq ".*" ? "" : " $arg";
1420
1421 $MAPWIDGET->add_command ("$cmd$arg", $text);
1157 }); 1422 }
1158 $MAPWIDGET->add_command ("use_skill $skill", "", sub { 1423 }
1424}
1425
1426sub conn::eof {
1427 $MAPWIDGET->clr_commands;
1428
1429 stop_game;
1430}
1431
1432sub update_floorbox {
1433 $CFClient::UI::ROOT->on_refresh ($FLOORBOX => sub {
1434 return unless $CONN;
1435
1436 $FLOORBOX->clear;
1437 $FLOORBOX->add (new CFClient::UI::Empty expand => 1);
1438
1439 my $count = 4;
1440 for (@{ $CONN->{container}{0} }) {
1441 if (--$count) {
1442 $FLOORBOX->add (new CFClient::UI::InventoryItem item => $_);
1443 } else {
1444 $FLOORBOX->add (new CFClient::UI::Label text => "More...");
1445 last;
1446 }
1159 }); 1447 }
1448 });
1449
1450 $WANT_REFRESH++;
1451}
1452
1453sub conn::container_add {
1454 my ($self, $tag, $items) = @_;
1455
1456 #d# print "container_add: container $tag ($self->{player}{tag})\n";
1457
1458 if ($tag == 0) {
1459 update_floorbox;
1460 $OPENCONT = 0;
1461 $INVR_LBL->set_text ("Floor");
1462 $INVR->set_items ($self->{container}{0});
1463 } elsif ($tag == $self->{player}{tag}) {
1464 $INVR_LBL->set_text ("Player");
1465 $INV->set_items ($self->{container}{$self->{player}{tag}})
1466 } else {
1467 $OPENCONT = $tag;
1468 $INVR_LBL->set_text (CFClient::UI::InventoryItem::_item_to_desc ($self->{item}->{$OPENCONT}));
1469 $INVR->set_items ($self->{container}{$tag});
1470 }
1471
1472 # $self-<{player}{tag} => player inv
1473 #use PApp::Util; warn PApp::Util::dumpval $self->{container}{$self->{player}{tag}};
1474}
1475
1476sub conn::container_clear {
1477 my ($self, $tag) = @_;
1478
1479 #d# print "container_clear: container $tag ($self->{player}{tag})\n";
1480
1481 if ($tag == 0) {
1482 update_floorbox;
1483 $OPENCONT = 0;
1484 $INVR_LBL->set_text ("Floor");
1485 $INVR->set_items ($self->{container}{0});
1486 } elsif ($tag == $self->{player}{tag}) {
1487 $INVR_LBL->set_text ("Player");
1488 $INV->set_items ($self->{container}{$tag})
1489 } else {
1490 $OPENCONT = $tag;
1491 $INVR_LBL->set_text (CFClient::UI::InventoryItem::_item_to_desc ($self->{item}->{$OPENCONT}));
1492 $INVR->set_items ($self->{container}{$tag});
1493 }
1494
1495# use PApp::Util; warn PApp::Util::dumpval $self->{container}{0};
1496}
1497
1498sub conn::item_delete {
1499 my ($self, @items) = @_;
1500
1501 for (@items) {
1502 #d# print "item_delete: $_->{tag} from $_->{container} ($self->{player}{tag})\n";
1503
1504 if ($_->{container} == 0) {
1505 update_floorbox;
1506 $OPENCONT = 0;
1507 $INVR_LBL->set_text ("Floor");
1508 $INVR->set_items ($self->{container}{0});
1509 } elsif ($_->{container} == $self->{player}{tag}) {
1510 $INVR_LBL->set_text ("Player");
1511 $INV->set_items ($self->{container}{$self->{player}{tag}})
1512 } else {
1513 $OPENCONT = $_->{container};
1514 $INVR_LBL->set_text (CFClient::UI::InventoryItem::_item_to_desc ($self->{item}->{$OPENCONT}));
1515 $INVR->set_items ($self->{container}{$_->{container}});
1516 }
1517 }
1518}
1519
1520sub conn::item_update {
1521 my ($self, $item) = @_;
1522
1523 #d# print "item_update: $item->{tag} in $item->{container} ($self->{player}{tag}) ($OPENCONT)\n";
1524
1525 if ($item->{tag} == $OPENCONT && not ($item->{flags} & Crossfire::Protocol::F_OPEN)) {
1526 $OPENCONT = 0;
1527 $INVR_LBL->set_text ("Floor");
1528 $INVR->set_items ($self->{container}{0});
1529
1530 $item->{widget}->update_item
1531 if $item->{widget};
1532 } else {
1533 if ($item->{container} == 0) {
1534 update_floorbox;
1535 $OPENCONT = 0;
1536 $INVR_LBL->set_text ("Floor");
1537 $INVR->set_items ($self->{container}{0});
1538 } elsif ($item->{container} == $self->{player}{tag}) {
1539 $INV->set_items ($self->{container}{$item->{container}})
1540 }
1160 } 1541 }
1161} 1542}
1162 1543
1163%SDL_CB = ( 1544%SDL_CB = (
1164 CFClient::SDL_QUIT => sub { 1545 CFClient::SDL_QUIT => sub {
1165 Event::unloop -1; 1546 Event::unloop -1;
1166 }, 1547 },
1167 CFClient::SDL_VIDEORESIZE => sub { 1548 CFClient::SDL_VIDEORESIZE => sub {
1168 }, 1549 },
1169 CFClient::SDL_VIDEOEXPOSE => \&refresh, 1550 CFClient::SDL_VIDEOEXPOSE => sub {
1551 $WANT_REFRESH++;
1552 },
1170 CFClient::SDL_ACTIVEEVENT => sub { 1553 CFClient::SDL_ACTIVEEVENT => sub {
1171# printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d# 1554# printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d#
1172 }, 1555 },
1173 CFClient::SDL_KEYDOWN => sub { 1556 CFClient::SDL_KEYDOWN => sub {
1174 if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) { 1557 if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) {
1178 video_init; 1561 video_init;
1179 } else { 1562 } else {
1180 CFClient::UI::feed_sdl_key_down_event ($_[0]); 1563 CFClient::UI::feed_sdl_key_down_event ($_[0]);
1181 } 1564 }
1182 }, 1565 },
1183 CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event, 1566 CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event,
1184 CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event, 1567 CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event,
1185 CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event, 1568 CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event,
1186 CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event, 1569 CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event,
1187 CFClient::SDL_USEREVENT => \&audio_music_finished, 1570 CFClient::SDL_USEREVENT => sub {
1571 if ($_[0]{code} == 1) {
1572 audio_channel_finished $_[0]{data1};
1573 } elsif ($_[0]{code} == 0) {
1574 audio_music_finished;
1575 }
1576 },
1188); 1577);
1189 1578
1190############################################################################# 1579#############################################################################
1191 1580
1192$SIG{INT} = $SIG{TERM} = sub { exit }; 1581$SIG{INT} = $SIG{TERM} = sub { exit };
1193 1582
1194$TILECACHE = CFClient::db_table "tilecache";
1195$FACEMAP = CFClient::db_table "facemap";
1196
1197CFClient::read_cfg "$Crossfire::VARDIR/pclientrc";
1198
1199my %DEF_CFG = (
1200 sdl_mode => 0,
1201 width => 640,
1202 height => 480,
1203 fullscreen => 0,
1204 fast => 0,
1205 fow_enable => 1,
1206 fow_intensity => 0.45,
1207 fow_smooth => 0,
1208 gui_fontsize => 1,
1209 log_fontsize => 1,
1210 gauge_fontsize => 1,
1211 gauge_size => 0.35,
1212 stat_fontsize => 1,
1213 mapsize => 100,
1214 host => "crossfire.schmorp.de",
1215 say_command => 'say',
1216 audio_enable => 1,
1217 bgm_enable => 1,
1218 bgm_volume => 0.25,
1219);
1220
1221while (my ($k, $v) = each %DEF_CFG) {
1222 $CFG->{$k} = $v unless exists $CFG->{$k};
1223}
1224
1225sdl_init;
1226
1227@SDL_MODES = reverse
1228 grep $_->[0] >= 640 && $_->[1] >= 480,
1229 CFClient::SDL_ListModes;
1230
1231@SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
1232
1233$CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
1234
1235{ 1583{
1584 local $SIG{__DIE__} = sub { CFClient::fatal $_[0] };
1585
1586 CFClient::read_cfg "$Crossfire::VARDIR/pclientrc";
1587
1588 $TILECACHE = CFClient::db_table "tilecache";
1589 $FACEMAP = CFClient::db_table "facemap";
1590
1591 my %DEF_CFG = (
1592 sdl_mode => 0,
1593 width => 640,
1594 height => 480,
1595 fullscreen => 0,
1596 fast => 0,
1597 map_scale => 0.5,
1598 fow_enable => 1,
1599 fow_intensity => 0.45,
1600 fow_smooth => 0,
1601 gui_fontsize => 1,
1602 log_fontsize => 1,
1603 gauge_fontsize=> 1,
1604 gauge_size => 0.35,
1605 stat_fontsize => 1,
1606 mapsize => 100,
1607 host => "crossfire.schmorp.de",
1608 say_command => 'say',
1609 audio_enable => 1,
1610 bgm_enable => 1,
1611 bgm_volume => 0.25,
1612 );
1613
1614 while (my ($k, $v) = each %DEF_CFG) {
1615 $CFG->{$k} = $v unless exists $CFG->{$k};
1616 }
1617
1618 sdl_init;
1619
1620 @SDL_MODES = reverse
1621 grep $_->[0] >= 640 && $_->[1] >= 480,
1622 CFClient::SDL_ListModes;
1623
1624 @SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
1625
1626 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
1627
1628 {
1236 my @fonts = map CFClient::find_rcfile "fonts/$_", qw( 1629 my @fonts = map CFClient::find_rcfile "fonts/$_", qw(
1237 DejaVuSans.ttf 1630 DejaVuSans.ttf
1238 DejaVuSansMono.ttf 1631 DejaVuSansMono.ttf
1239 DejaVuSans-Bold.ttf 1632 DejaVuSans-Bold.ttf
1240 DejaVuSansMono-Bold.ttf 1633 DejaVuSansMono-Bold.ttf
1241 DejaVuSans-Oblique.ttf 1634 DejaVuSans-Oblique.ttf
1242 DejaVuSansMono-Oblique.ttf 1635 DejaVuSansMono-Oblique.ttf
1243 DejaVuSans-BoldOblique.ttf 1636 DejaVuSans-BoldOblique.ttf
1244 DejaVuSansMono-BoldOblique.ttf 1637 DejaVuSansMono-BoldOblique.ttf
1245 ); 1638 );
1246 1639
1247 CFClient::add_font $_ for @fonts; 1640 CFClient::add_font $_ for @fonts;
1248 1641
1642 CFClient::pango_init;
1643
1249 $FONT_PROP = new_from_file CFClient::Font $fonts[0]; 1644 $FONT_PROP = new_from_file CFClient::Font $fonts[0];
1250 $FONT_FIXED = new_from_file CFClient::Font $fonts[1]; 1645 $FONT_FIXED = new_from_file CFClient::Font $fonts[1];
1251 1646
1252 $FONT_PROP->make_default; 1647 $FONT_PROP->make_default;
1253} 1648 }
1254 1649
1650# compare mono (ft) vs. rgba (cairo)
1651# ft - 1.8s, cairo 3s, even in alpha-only mode
1652# for my $rgba (0..1) {
1653# my $t1 = Time::HiRes::time;
1654# for (1..1000) {
1655# my $layout = CFClient::Layout->new ($rgba);
1656# $layout->set_text ("hallo" x 100);
1657# $layout->render;
1658# }
1659# my $t2 = Time::HiRes::time;
1660# warn $t2-$t1;
1661# }
1662
1255video_init; 1663 video_init;
1256audio_init; 1664 audio_init;
1665}
1257 1666
1258Event::loop; 1667Event::loop;
1259 1668
1260END { CFClient::SDL_Quit } 1669END { CFClient::SDL_Quit }
1261 1670
1671=head1 pclient - Crossfire+ and Crossfire game client
1262 1672
1673Pclient is a Crossfire+ and Crossfire game client.
1674
1675=head2 Features
1676
1677=over 4
1678
1679=item Fullscreen Map
1680
1681PClient can uses a fullscreen map, which greatly enhances how much of the
1682game world you can see.
1683
1684=item Persistent Map Cache (Crossfire+ only)
1685
1686PClient can persistently cache all map data it received from the
1687server. This not only allows it to display an overview map, but also
1688ensures that once-explored areas will be available the next time you want
1689to explore more.
1690
1691=item Hardware acceleration
1692
1693Unlike most Crossfire clients, PClient take advantage of OpenGL hardware
1694acceleration. Most modern graphics cards have difficulties with 2D
1695acceleration, while 3D graphics is accelerated well.
1696
1697=item No arbitrary limits
1698
1699Unlike other Crossfire clients, pclient does not suffer from arbitrary
1700limits (like a fixed amount of face numbers). There are still limits, but
1701they are not arbitrarily low :)
1702
1703=back
1704
1705=head1 USAGE
1706
1707=head2 The Map
1708
1709The map is always displayed in the background, behind all other windows and UI elements.
1710
1711#TODO# middle-click scrolls
1712#
1713# keys:
1714#
1715# a apply
1716# keypad moves, kp_5 applies ranged attack to self
1717
1718Starting to type enters the I<completion mode>. In that mode, you can type
1719abbreviations or commands and have them executed as soon as they match a
1720valid command. This is best explained by a few examples:
1721
1722Typing B<climb> will display a list of commands with I<climb> in their
1723name, such as I<ready_skill climbing> and I<use_skill climbing>.
1724
1725You can abbreviate commands by typing only the first character of every
1726word. For example, typing I<iwor> will likely select I<invoke word of
1727recall>, while I<ccfo> will select I<cast create food>. Likewise, I<rscli>
1728will likely select I<ready_skill climbing> and I<usl> will give you
1729I<use_skill levitation>.
1730
1731=head2 The map overview
1732
1733#TODO#
1734
1735=head2 The Status area in the lower right corner
1736
1737#TODO#
1738
1739=head2 The I<Statistics>/I>Stats> window
1740
1741#TODO#
1742
1743=head1 FAQ
1744
1745=over 4
1746
1747=item The client is very sluggish and slow, what can I do about this?
1748
1749Most likely, you don't have accelerated OpenGL support. Try to find a
1750newer driver, or a driver from your hardware vendor, that features OpenGL
1751support.
1752
1753If this is not an option, the following Setup options reduce the load and
1754will likely make the client playable with sofwtare rendering (it will
1755still be slow, though):
1756
1757=over 4
1758
1759=item B<Video Mode> should be set as low as possible (e.g. 640x480)
1760
1761=item Enable B<Fast & Ugly> mode
1762
1763=item Disable B<Fog of War>
1764
1765=item Increase B<Map Scale>
1766
1767=back
1768
1769=back
1770
1771=head1 AUTHOR
1772
1773Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
1774
1775
1776

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines