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.145 by root, Wed Apr 19 20:46:45 2006 UTC vs.
Revision 1.199 by root, Mon May 8 15:12:39 2006 UTC

1#!/opt/bin/perl 1#!/opt/bin/perl
2 2
3use strict; 3use strict;
4use utf8; 4use utf8;
5 5
6BEGIN {
7 if (%PAR::LibCache) {
8 @INC = grep ref, @INC; # weed out all paths except pars loader refs
9
10 while (my ($filename, $zip) = each %PAR::LibCache) {
11 for ($zip->memberNames) {
12 next unless /^\/root\/(.*)/;
13 $zip->extractMember ($_, "$ENV{PAR_TEMP}/$1")
14 unless -e "$ENV{PAR_TEMP}/$1";
15 }
16 }
17
18 unshift @INC, $ENV{PAR_TEMP};
19
20 if ($^O eq "MSWin32") {
21 $ENV{GTK_RC_FILES} = "$ENV{PAR_TEMP}/share/themes/MS-Windows/gtk-2.0/gtkrc";
22 }
23 }
24}
25
26# need to do it again because that pile of garbage called PAR nukes it before main
27unshift @INC, $ENV{PAR_TEMP};
28
6use Time::HiRes 'time'; 29use Time::HiRes 'time';
7use Event; 30use Event;
8
9use SDL::Event;
10
11use SDL::Mixer;
12use SDL::Sound;
13use SDL::Music;
14
15use SDL::OpenGL;
16 31
17use Crossfire; 32use Crossfire;
18use Crossfire::Protocol; 33use Crossfire::Protocol;
19 34
20use Compress::LZF; 35use Compress::LZF;
21 36
22use CFClient; 37use CFClient;
23use CFClient::UI; 38use CFClient::UI;
24use CFClient::MapWidget; 39use CFClient::MapWidget;
40
41$Event::DIED = sub {
42 CFClient::error $_[1];
43};
44
45#$SIG{__WARN__} = sub { Carp::cluck $_[0] };#d#
25 46
26our $VERSION = '0.1'; 47our $VERSION = '0.1';
27 48
28my $MAX_FPS = 60; 49my $MAX_FPS = 60;
29my $MIN_FPS = 5; # unused as of yet 50my $MIN_FPS = 5; # unused as of yet
45our $WIDTH; 66our $WIDTH;
46our $HEIGHT; 67our $HEIGHT;
47our $FULLSCREEN; 68our $FULLSCREEN;
48our $FONTSIZE; 69our $FONTSIZE;
49 70
71our $FONT_PROP;
72our $FONT_FIXED;
73
50our $MAP; 74our $MAP;
75our $MAPMAP;
51our $MAPWIDGET; 76our $MAPWIDGET;
52our $BUTTONBAR; 77our $BUTTONBAR;
53our $LOGVIEW; 78our $LOGVIEW;
54our $CONSOLE; 79our $CONSOLE;
55our $METASERVER; 80our $METASERVER;
81our $LOGIN_BUTTON;
56 82
83our $FLOORBOX;
57our $GAUGES; 84our $GAUGES;
85our $STATWIDS;
58 86
59our $SDL_ACTIVE; 87our $SDL_ACTIVE;
60our $SDL_EV;
61our %SDL_CB; 88our %SDL_CB;
62 89
63our $SDL_MIXER; 90our $SDL_MIXER;
64our @SOUNDS; # event => file mapping 91our @SOUNDS; # event => file mapping
65our %AUDIO_CHUNKS; # audio files 92our %AUDIO_CHUNKS; # audio files
66 93
67our $ALT_ENTER_MESSAGE; 94our $ALT_ENTER_MESSAGE;
68our $STATUS_LINE; 95our $STATUS_LINE;
69our $DEBUG_STATUS; 96our $DEBUG_STATUS;
70 97
98our $INVWIN;
99our $INV;
100
71sub status { 101sub status {
72 $STATUS_LINE->set_text ($_[0]); 102 $STATUS_LINE->set_text ($_[0]);
73 $STATUS_LINE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h} - $STATUS_LINE->{h}); 103 $STATUS_LINE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h} - $STATUS_LINE->{h});
74} 104}
75 105
77 $DEBUG_STATUS->set_text ($_[0]); 107 $DEBUG_STATUS->set_text ($_[0]);
78 $DEBUG_STATUS->move ($WIDTH - $DEBUG_STATUS->{w}, 0, $DEBUG_STATUS->{w}, $DEBUG_STATUS->{h}); 108 $DEBUG_STATUS->move ($WIDTH - $DEBUG_STATUS->{w}, 0, $DEBUG_STATUS->{w}, $DEBUG_STATUS->{h});
79} 109}
80 110
81sub start_game { 111sub start_game {
112 $LOGIN_BUTTON->set_text ("Logout");
113
82 status "logging in..."; 114 status "logging in...";
83 115
84 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32; 116 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
85 117
86 $MAPCACHE = CFClient::db_table "mapcache_$CFG->{host}"; 118 $MAPCACHE = CFClient::db_table "mapcache_$CFG->{host}";
87 119
88 $MAP = new CFClient::Map $mapsize, $mapsize; 120 $MAP = new CFClient::Map $mapsize, $mapsize;
89 121
90 my ($host, $port) = split /:/, $CFG->{host}; 122 my ($host, $port) = split /:/, $CFG->{host};
91 123
92 $CONN = new conn 124 $CONN = eval {
125 new conn
93 host => $host, 126 host => $host,
94 port => $port || 13327, 127 port => $port || 13327,
95 user => $CFG->{user}, 128 user => $CFG->{user},
96 pass => $CFG->{password}, 129 pass => $CFG->{password},
97 mapw => $mapsize, 130 mapw => $mapsize,
98 maph => $mapsize, 131 maph => $mapsize,
132 ;
99 ; 133 };
100 134
135 if ($CONN) {
101 status "login successful"; 136 status "login successful";
102 137
103 CFClient::lowdelay fileno $CONN->{fh}; 138 CFClient::lowdelay fileno $CONN->{fh};
139 } else {
140 status "unable to connect";
141 stop_game();
142 }
104} 143}
105 144
106sub stop_game { 145sub stop_game {
146 status "connection closed";
147 $LOGIN_BUTTON->set_text ("Login");
107 undef $CONN; 148 undef $CONN;
108} 149}
109 150
110sub client_setup { 151sub client_setup {
111 my $dialog = new CFClient::UI::FancyFrame 152 my $dialog = new CFClient::UI::FancyFrame
153 title => "Client Setup",
112 child => (my $vbox = new CFClient::UI::VBox); 154 child => (my $vbox = new CFClient::UI::VBox);
113 $vbox->add (new CFClient::UI::Label valign => 0, align => 0, text => "Client Setup");
114 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]); 155 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
115 156
116 $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode"); 157 $table->add (0, 0, new CFClient::UI::Label valign => 0, align => 1, text => "Video Mode");
117 $table->add (1, 0, my $hbox = new CFClient::UI::HBox); 158 $table->add (1, 0, my $hbox = new CFClient::UI::HBox);
118 159
119 $hbox->add (my $mode_slider = new CFClient::UI::Slider expand => 1, req_w => 100, range => [$CFG->{sdl_mode}, 0, scalar @SDL_MODES, 1]); 160 $hbox->add (my $mode_slider = new CFClient::UI::Slider expand => 1, req_w => 100, range => [$CFG->{sdl_mode}, 0, scalar @SDL_MODES, 1]);
120 $hbox->add (my $mode_label = new CFClient::UI::Label valign => 0, height => 0.8); 161 $hbox->add (my $mode_label = new CFClient::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999");
121 162
122 $mode_slider->connect (changed => sub { 163 $mode_slider->connect (changed => sub {
123 my ($self, $value) = @_; 164 my ($self, $value) = @_;
124 165
125 $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value; 166 $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
126 $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]}); 167 $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]});
127 }); 168 });
128 $mode_slider->emit (changed => $mode_slider->{range}[0]); 169 $mode_slider->emit (changed => $mode_slider->{range}[0]);
129 170
171 my $row = 1;
172
130 $table->add (0, 1, new CFClient::UI::Label valign => 0, align => 1, text => "Fullscreen"); 173 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fullscreen");
131 $table->add (1, 1, new CFClient::UI::CheckBox state => $CFG->{fullscreen}, connect_changed => sub { 174 $table->add (1, $row++, new CFClient::UI::CheckBox
175 state => $CFG->{fullscreen},
176 tooltip => "Bring the client into fullscreen mode",
177 connect_changed => sub {
132 my ($self, $value) = @_; 178 my ($self, $value) = @_;
133 $CFG->{fullscreen} = $value; 179 $CFG->{fullscreen} = $value;
180 }
134 }); 181 );
135 182
136 $table->add (0, 2, new CFClient::UI::Label valign => 0, align => 1, text => "Fast & Ugly"); 183 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
137 $table->add (1, 2, new CFClient::UI::CheckBox state => $CFG->{fast}, connect_changed => sub { 184 $table->add (1, $row++, new CFClient::UI::CheckBox
185 state => $CFG->{fast},
186 tooltip => "Lower the visual quality considerably to speed up rendering.",
187 connect_changed => sub {
138 my ($self, $value) = @_; 188 my ($self, $value) = @_;
139 $CFG->{fast} = $value; 189 $CFG->{fast} = $value;
190 }
140 }); 191 );
141 192
193 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Map Scale");
194 $table->add (1, $row++, new CFClient::UI::Slider
195 range => [$CFG->{map_scale}, 0.25, 2, 0.05],
196 tooltip => "Enlarge or shrink the displayed map",
197 connect_changed => sub {
198 my ($self, $value) = @_;
199 $CFG->{map_scale} = 0.05 * int $value / 0.05;
200 }
201 );
202
142 $table->add (0, 3, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War"); 203 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Fog of War");
143 $table->add (1, 3, new CFClient::UI::CheckBox state => $CFG->{fow_enable}, connect_changed => sub { 204 $table->add (1, $row++, new CFClient::UI::CheckBox
205 state => $CFG->{fow_enable},
206 tooltip => "Fog-of-War marks areas that cannot be seen by the player",
207 connect_changed => sub {
144 my ($self, $value) = @_; 208 my ($self, $value) = @_;
145 $CFG->{fow_enable} = $value; 209 $CFG->{fow_enable} = $value;
210 }
146 }); 211 );
147 212
148 $table->add (0, 4, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Intensity"); 213 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Intensity");
149 $table->add (1, 4, new CFClient::UI::Slider range => [$CFG->{fow_intensity}, 0, 1 + 0.001, 0.001], connect_changed => sub { 214 $table->add (1, $row++, new CFClient::UI::Slider
215 range => [$CFG->{fow_intensity}, 0, 1 + 0.001, 0.001],
216 tooltip => "The higher the intensity, the lighter the Fog-of-War color",
217 connect_changed => sub {
150 my ($self, $value) = @_; 218 my ($self, $value) = @_;
151 $CFG->{fow_intensity} = $value; 219 $CFG->{fow_intensity} = $value;
220 }
152 }); 221 );
153 222
154 $table->add (0, 5, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Smooth"); 223 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "FoW Smooth");
155 $table->add (1, 5, new CFClient::UI::CheckBox state => $CFG->{fow_smooth}, connect_changed => sub { 224 $table->add (1, $row++, new CFClient::UI::CheckBox
225 state => $CFG->{fow_smooth},
226 tooltip => "Smooth the Fog-of-War a bit to make it more realistic",
227 connect_changed => sub {
156 my ($self, $value) = @_; 228 my ($self, $value) = @_;
157 $CFG->{fow_smooth} = $value; 229 $CFG->{fow_smooth} = $value;
158 status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFClient::GL_VERSION < 1.2; 230 status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFClient::GL_VERSION < 1.2;
231 }
159 }); 232 );
160 233
161 $table->add (0, 6, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize"); 234 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
162 $table->add (1, 6, new CFClient::UI::Slider range => [$CFG->{gui_fontsize}, 0.7, 1.7, 0.1], connect_changed => sub { 235 $table->add (1, $row++, new CFClient::UI::Slider
236 range => [$CFG->{gui_fontsize}, 0.5, 2, 0.1],
237 tooltip => "The font size used by most GUI elements",
238 connect_changed => sub {
163 $CFG->{gui_fontsize} = 0.1 * int $_[1] * 10; 239 $CFG->{gui_fontsize} = 0.1 * int $_[1] * 10;
164# $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize}; 240# $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
241 }
165 }); 242 );
166 243
167 $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Server Log Fontsize"); 244 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Server Log Fontsize");
168 $table->add (1, 7, new CFClient::UI::Slider range => [$CFG->{log_fontsize}, 0.7, 1.7, 0.1], connect_changed => sub { 245 $table->add (1, $row++, new CFClient::UI::Slider
246 range => [$CFG->{log_fontsize}, 0.5, 2, 0.1],
247 tooltip => "The font size used by the server log window only",
248 connect_changed => sub {
169 $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = 0.1 * int $_[1] * 10); 249 $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = 0.1 * int $_[1] * 10);
250 }
170 }); 251 );
171 252
172 $table->add (1, 8, new CFClient::UI::Button expand => 1, align => 0, text => "Apply", connect_activate => sub { 253 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Stats Fontsize");
254
255 $table->add (1, $row++, new CFClient::UI::Slider
256 range => [$CFG->{stat_fontsize}, 0.5, 2, 0.1],
257 tooltip => "The font size used by the statistics window only",
258 connect_changed => sub {
259 $CFG->{stat_fontsize} = 0.1 * int $_[1] * 10;
260 &set_stats_window_fontsize;
261 }
262 );
263
264 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge size");
265 $table->add (1, $row++, new CFClient::UI::Slider
266 range => [$CFG->{gauge_size}, 0.2, 0.8, 0.02],
267 tooltip => "Adjust the size of the stats gauges at the bottom right",
268 connect_changed => sub {
269 $CFG->{gauge_size} = $_[1];
270 my $h = int $HEIGHT * $CFG->{gauge_size};
271 $GAUGES->{win}->set_size ($WIDTH, $h);
272 $GAUGES->{win}->move (0, $HEIGHT - $h);
273 }
274 );
275
276 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
277 $table->add (1, $row++, new CFClient::UI::Slider
278 range => [$CFG->{gauge_fontsize}, 0.5, 2.0, 0.1],
279 tooltip => "Adjusts the fontsize of the gauges at the bottom right",
280 connect_changed => sub {
281 $CFG->{gauge_fontsize} = 0.1 * int $_[1] * 10;
282 &set_gauge_window_fontsize;
283 }
284 );
285
286 $table->add (1, $row++, new CFClient::UI::Button
287 expand => 1, align => 0, text => "Apply",
288 tooltip => "Apply the video settings",
289 connect_activate => sub {
173 video_shutdown (); 290 video_shutdown ();
174 video_init (); 291 video_init ();
292 }
175 }); 293 );
176 294
177 $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Audio Enable"); 295 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Audio Enable");
178 $table->add (1, 9, new CFClient::UI::CheckBox state => $CFG->{audio_enable}, connect_changed => sub { 296 $table->add (1, $row++, new CFClient::UI::CheckBox
297 state => $CFG->{audio_enable},
298 tooltip => "If enabled, sound effects and music will be played. If disabled, no audio will be used and the soundcard will not be opened.",
299 connect_changed => sub {
179 $CFG->{audio_enable} = $_[1]; 300 $CFG->{audio_enable} = $_[1];
301 }
180 }); 302 );
181# $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Effects Volume"); 303# $table->add (0, 9, new CFClient::UI::Label valign => 0, align => 1, text => "Effects Volume");
182# $table->add (1, 8, new CFClient::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], connect_changed => sub { 304# $table->add (1, 8, new CFClient::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], connect_changed => sub {
183# $CFG->{effects_volume} = $_[1]; 305# $CFG->{effects_volume} = $_[1];
184# }); 306# });
185 $table->add (0, 10, new CFClient::UI::Label valign => 0, align => 1, text => "Background Music"); 307 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Background Music");
186 $table->add (1, 10, my $hbox = new CFClient::UI::HBox); 308 $table->add (1, $row++, my $hbox = new CFClient::UI::HBox);
187 $hbox->add (new CFClient::UI::CheckBox expand => 1, state => $CFG->{bgm_enable}, connect_changed => sub { 309 $hbox->add (new CFClient::UI::CheckBox
310 expand => 1, state => $CFG->{bgm_enable},
311 tooltip => "Enable background music playing",
312 connect_changed => sub {
188 $CFG->{bgm_enable} = $_[1]; 313 $CFG->{bgm_enable} = $_[1];
314 }
189 }); 315 );
190 $hbox->add (new CFClient::UI::Slider expand => 1, range => [$CFG->{bgm_volume}, 0, 128, 1], connect_changed => sub { 316 $hbox->add (new CFClient::UI::Slider
317 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0.1],
318 tooltip => "The volume of the background music",
319 connect_changed => sub {
191 $CFG->{bgm_volume} = $_[1]; 320 $CFG->{bgm_volume} = $_[1];
192 $SDL_MIXER->music_volume ($_[1]); 321 CFClient::MixMusic::volume $_[1] * 128;
322 }
193 }); 323 );
194 324
195 $table->add (1, 11, new CFClient::UI::Button expand => 1, align => 0, text => "Apply", connect_activate => sub { 325 $table->add (1, $row++, new CFClient::UI::Button
326 expand => 1, align => 0, text => "Apply",
327 tooltip => "Apply the audio settings",
328 connect_activate => sub {
196 audio_shutdown (); 329 audio_shutdown ();
197 audio_init (); 330 audio_init ();
331 }
198 }); 332 );
333
334 $table->add (0, $row, new CFClient::UI::Label valign => 0, align => 1, text => "Communication cmd");
335 $table->add (1, $row++, my $saycmd = new CFClient::UI::Entry
336 text => $CFG->{say_command},
337 tooltip => "This is the command that will be used if you write a line in the message window entry. "
338 ."Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
339 ."But you could also set it to 'tell &lt;playername&gt;' to only chat with that user.",
340 connect_changed => sub {
341 my ($self, $value) = @_;
342 $CFG->{say_command} = $value;
343 }
344 );
199 345
200 $dialog 346 $dialog
347}
348
349sub set_stats_window_fontsize {
350 for (values %{$STATWIDS}) {
351 $_->set_fontsize ($::CFG->{stat_fontsize});
352 }
353}
354
355sub set_gauge_window_fontsize {
356 for (map { $GAUGES->{$_} } grep { $_ ne 'win' } keys %{$GAUGES}) {
357 $_->set_fontsize ($::CFG->{gauge_fontsize});
358 }
359
360# local $GAUGES->{win}{parent};#d#
361# use PApp::Util; open D, ">:utf8", "d"; print D PApp::Util::dumpval $GAUGES->{win}; close D;
362}
363
364sub make_gauge_window {
365 my $gh = int ($HEIGHT * $CFG->{gauge_size});
366# my $gw = int ($WIDTH * $CFG->{gauge_w_size});
367
368 my $win = new CFClient::UI::Frame (
369 y => $HEIGHT - $gh, x => 0, user_w => $WIDTH, user_h => $gh
370 );
371 $win->add (my $hbox = new CFClient::UI::HBox
372 children => [
373 (new CFClient::UI::HBox expand => 1),
374 ($FLOORBOX = new CFClient::UI::VBox),
375 (my $vbox = new CFClient::UI::VBox),
376 ],
377 );
378
379 $vbox->add (new CFClient::UI::HBox
380 expand => 1,
381 children => [
382 (new CFClient::UI::Empty expand => 1),
383 (my $hb = new CFClient::UI::HBox),
384 ],
385 );
386
387 $hb->add (my $hg = new CFClient::UI::Gauge type => 'hp',
388 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.");
389 $hb->add (my $mg = new CFClient::UI::Gauge type => 'mana',
390 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.");
391 $hb->add (my $gg = new CFClient::UI::Gauge type => 'grace',
392 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.");
393 $hb->add (my $fg = new CFClient::UI::Gauge type => 'food',
394 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.");
395
396 $vbox->add (my $exp = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
397 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.");
398 $vbox->add (my $rng = new CFClient::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1,
399 tooltip => "Ranged attack - how you attack when you press shift-cursor (spell, skill, weapon etc.)");
400
401 $GAUGES = {
402 exp => $exp, win => $win, range => $rng,
403 food => $fg, mana => $mg, hp => $hg, grace => $gg
404 };
405
406 &set_gauge_window_fontsize;
407
408 $win
409}
410
411sub make_stats_window {
412 my $tgw = new CFClient::UI::FancyFrame x => $WIDTH * 2/5, y => 0, title => "Stats";
413
414 $tgw->add (new CFClient::UI::Window child => my $vb = new CFClient::UI::VBox);
415 $vb->add ($STATWIDS->{title} = new CFClient::UI::Label valign => 0, align => -1, text => "Title:", expand => 1);
416 $vb->add ($STATWIDS->{map} = new CFClient::UI::Label valign => 0, align => -1, text => "Map:", expand => 1);
417
418 $vb->add (my $hb = new CFClient::UI::HBox expand => 1);
419
420 $hb->add (my $tbl = new CFClient::UI::Table expand => 1);
421
422 my $black = [0, 0, 0];
423
424 for (
425 [0, 0, st_str => "Str", 30, "Physical Strength, determines damage dealt with weapons, how much you can carry, and how often you can attack"],
426 [0, 1, st_dex => "Dex", 30, "Dexterity, your physical agility. Determines chance of being hit and affects armor class and speed"],
427 [0, 2, st_con => "Con", 30, "Constitution, physical health and toughness. Determines how many healthpoints you can have"],
428 [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"],
429 [0, 4, st_wis => "Wis", 30, "Wisdom, the ability to learn and use divine magic (prayers). Determines how many grace points you can have"],
430 [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"],
431 [0, 6, st_cha => "Cha", 30, "Charisma, how well you are received by NPCs. Affects buying and selling prices in shops."],
432
433 [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."],
434 [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."],
435 [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."],
436 [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."],
437 [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."],
438 [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."],
439 ) {
440 my ($col, $row, $id, $label, $template, $tooltip) = @$_;
441
442 $tbl->add ($col , $row, $STATWIDS->{$id} = new CFClient::UI::Label
443 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0, align => +1, template => $template, tooltip => $tooltip);
444 $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFClient::UI::Label
445 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $black, valign => 0, align => -1, text => $label, tooltip => $tooltip);
446 }
447
448 $hb->add (my $tbl2 = new CFClient::UI::Table expand => 1);
449
450 my $row = 0;
451 my $col = 0;
452
453 my %resist_names = (
454 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.)",
455 holyw => "Holy Word (resistance you against getting the fear when someone whose god doesn't like you spells the holy word on you.)",
456 conf => "Confusion (If you are hit by confusion you will move into random directions, and likely into monsters.)",
457 fire => "Fire (just your resistance to fire spells like burning hands, dragonbreath, meteor swarm fire, ...)",
458 depl => "Depletion (some monsters and other effects can cause stats depletion)",
459 magic => "Magic (resistance to magic spells like magic missile or similar)",
460 drain => "Draining (some monsters (e.g. vampires) and other effects can steal experience)",
461 acid => "Acid (resistance to acid, acid hurts pretty much and also corrodes your weapons)",
462 pois => "Poison (resistance to getting poisoned)",
463 para => "Paralysation (this resistance affects the chance you get paralysed)",
464 deat => "Death (resistance against death spells)",
465 phys => "Physical (this is the resistance against physical attacks, like when a monster hit you in melee combat)",
466 blind => "Blind (blind resistance affects the chance of a successful blinding attack)",
467 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)",
468 tund => "Turn undead",
469 elec => "Electricity (resistance againt electricity, spells like large lightning, small lightning, ...)",
470 cold => "Cold (this is your resistance against cold spells like icestorm, snowstorm, ...)",
471 ghit => "Ghost hit (special attack used by ghosts and ghost-like beings)",
472 );
473 for (qw/slow holyw conf fire depl magic
474 drain acid pois para deat phys
475 blind fear tund elec cold ghit/)
476 {
477 $tbl2->add ($col, $row,
478 $STATWIDS->{"res_$_"} =
479 new CFClient::UI::Label
480 font => $FONT_FIXED,
481 template => "-100%",
482 align => +1,
483 valign => 0,
484 can_events => 1,
485 can_hover => 1,
486 tooltip => $resist_names{$_},
487 );
488 $tbl2->add ($col + 1, $row, new CFClient::UI::Image
489 font => $FONT_FIXED,
490 can_hover => 1,
491 can_events => 1,
492 image => "ui/resist/resist_$_.png",
493 tooltip => $resist_names{$_},
494 );
495
496 $row++;
497 if ($row % 6 == 0) {
498 $col += 2;
499 $row = 0;
500 }
501 }
502
503 &set_stats_window_fontsize;
504 update_stats_window ({});
505
506 $tgw
507}
508
509sub formsep {
510 reverse join ",", grep length, split /(...)/, reverse $_[0] * 1
511}
512
513sub update_stats_window {
514 my ($stats) = @_;
515
516 # i love text protocols!!!
517 my $hp = $stats->{Crossfire::Protocol::CS_STAT_HP} * 1;
518 my $hp_m = $stats->{Crossfire::Protocol::CS_STAT_MAXHP} * 1;
519 my $sp = $stats->{Crossfire::Protocol::CS_STAT_SP} * 1;
520 my $sp_m = $stats->{Crossfire::Protocol::CS_STAT_MAXSP} * 1;
521 my $fo = $stats->{Crossfire::Protocol::CS_STAT_FOOD} * 1;
522 my $fo_m = 999;
523 my $gr = $stats->{Crossfire::Protocol::CS_STAT_GRACE} * 1;
524 my $gr_m = $stats->{Crossfire::Protocol::CS_STAT_MAXGRACE} * 1;
525
526 $GAUGES->{hp} ->set_value ($hp, $hp_m);
527 $GAUGES->{mana} ->set_value ($sp, $sp_m);
528 $GAUGES->{food} ->set_value ($fo, $fo_m);
529 $GAUGES->{grace} ->set_value ($gr, $gr_m);
530 $GAUGES->{exp} ->set_text ("Exp: " . (formsep $stats->{Crossfire::Protocol::CS_STAT_EXP64})
531 . " (lvl " . ($stats->{Crossfire::Protocol::CS_STAT_LEVEL} * 1) . ")");
532 my $rng = $stats->{Crossfire::Protocol::CS_STAT_RANGE};
533 $rng =~ s/^Range: //; # thank you so much dear server
534 $GAUGES->{range} ->set_text ("Rng: " . $rng);
535 my $title = $stats->{Crossfire::Protocol::CS_STAT_TITLE};
536 $title =~ s/^Player: //;
537 $STATWIDS->{title} ->set_text ("Title: " . $title);
538
539 $STATWIDS->{st_str} ->set_text (sprintf "%d", $stats->{5});
540 $STATWIDS->{st_dex} ->set_text (sprintf "%d", $stats->{8});
541 $STATWIDS->{st_con} ->set_text (sprintf "%d", $stats->{9});
542 $STATWIDS->{st_int} ->set_text (sprintf "%d", $stats->{6});
543 $STATWIDS->{st_wis} ->set_text (sprintf "%d", $stats->{7});
544 $STATWIDS->{st_pow} ->set_text (sprintf "%d", $stats->{22});
545 $STATWIDS->{st_cha} ->set_text (sprintf "%d", $stats->{10});
546 $STATWIDS->{st_wc} ->set_text (sprintf "%d", $stats->{13});
547 $STATWIDS->{st_ac} ->set_text (sprintf "%d", $stats->{14});
548 $STATWIDS->{st_dam} ->set_text (sprintf "%d", $stats->{15});
549 $STATWIDS->{st_arm} ->set_text (sprintf "%d", $stats->{16});
550 $STATWIDS->{st_spd} ->set_text (sprintf "%.1f", $stats->{Crossfire::Protocol::CS_STAT_SPEED});
551 $STATWIDS->{st_wspd}->set_text (sprintf "%.1f", $stats->{Crossfire::Protocol::CS_STAT_WEAP_SP});
552
553 my %tbl = (
554 phys => 100,
555 magic => 101,
556 fire => 102,
557 elec => 103,
558 cold => 104,
559 conf => 105,
560 acid => 106,
561 drain => 107,
562 ghit => 108,
563 pois => 109,
564 slow => 110,
565 para => 111,
566 tund => 112,
567 fear => 113,
568 depl => 113,
569 deat => 115,
570 holyw => 116,
571 blind => 117
572 );
573
574 for (keys %tbl) {
575 $STATWIDS->{"res_$_"}->set_text (sprintf "%d%", $stats->{$tbl{$_}});
576 }
577
201} 578}
202 579
203sub metaserver_dialog { 580sub metaserver_dialog {
204 my $dialog = new CFClient::UI::FancyFrame 581 my $dialog = new CFClient::UI::FancyFrame
582 title => "Server List",
205 child => (my $vbox = new CFClient::UI::VBox); 583 child => (my $vbox = new CFClient::UI::VBox);
206 584
207 $vbox->add ($dialog->{table} = new CFClient::UI::Table); 585 $vbox->add ($dialog->{table} = new CFClient::UI::Table);
208 586
209 $dialog 587 $dialog
210} 588}
589
590my $METASERVER_ATIME;
211 591
212sub update_metaserver { 592sub update_metaserver {
213 my ($HOST) = @_; 593 my ($HOST) = @_;
214 594
215 status "fetching metaserver list..."; 595 return if $METASERVER_ATIME > time;
596 $METASERVER_ATIME = time + 60;
597
598 my $table = $METASERVER->{table};
599 $table->clear;
600 $table->add (0, 0, my $label = new CFClient::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
216 601
217 my $buf; 602 my $buf;
218 603
219 my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0; 604 my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0;
605
606 unless ($fh) {
607 $label->set_text ("unable to contact metaserver: $!");
608 return;
609 }
220 610
221 Event->io (fd => $fh, poll => 'r', cb => sub { 611 Event->io (fd => $fh, poll => 'r', cb => sub {
222 my $res = sysread $fh, $buf, 8192, length $buf; 612 my $res = sysread $fh, $buf, 8192, length $buf;
223 613
224 if (!defined $res) { 614 if (!defined $res) {
225 $_[0]->w->cancel; 615 $_[0]->w->cancel;
226 status "metaserver: $!"; 616 $label->set_text ("error while retrieving server list: $!");
227 } elsif ($res == 0) { 617 } elsif ($res == 0) {
228 $_[0]->w->cancel; 618 $_[0]->w->cancel;
229 status "server list retrieved"; 619 status "server list retrieved";
230 620
231 my $table = $METASERVER->{table}; 621 utf8::decode $buf if utf8::valid $buf;
232 622
233 $table->clear; 623 $table->clear;
234 624
235 my @col = qw(Use #Users Host Uptime Version Description); 625 my @col = qw(Use #Users Host Uptime Version Description);
236 $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_]) 626 $table->add ($_, 0, new CFClient::UI::Label align => 0, fg => [1, 1, 0], text => $col[$_])
260 $m = [$users, $host, $uptime, $version, $desc]; 650 $m = [$users, $host, $uptime, $version, $desc];
261 651
262 $y++; 652 $y++;
263 653
264 $table->add (0, $y, new CFClient::UI::VBox children => [ 654 $table->add (0, $y, new CFClient::UI::VBox children => [
265 (new CFClient::UI::Button text => " ", connect_activate => sub { 655 (new CFClient::UI::Button text => "Use", connect_activate => sub {
266 $HOST->set_text ($CFG->{host} = $host); 656 $HOST->set_text ($CFG->{host} = $host);
267 }), 657 }),
268 (new CFClient::UI::Empty expand => 1), 658 (new CFClient::UI::Empty expand => 1),
269 ]); 659 ]);
270 660
275 }); 665 });
276} 666}
277 667
278sub server_setup { 668sub server_setup {
279 my $dialog = new CFClient::UI::FancyFrame 669 my $dialog = new CFClient::UI::FancyFrame
670 title => "Server Setup",
280 child => (my $vbox = new CFClient::UI::VBox); 671 child => (my $vbox = new CFClient::UI::VBox);
281 672
282 $vbox->add (new CFClient::UI::Label valign => 0, align => 0, text => "Server Setup");
283 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]); 673 $vbox->add (my $table = new CFClient::UI::Table expand => 1, col_expand => [0, 1]);
284 $table->add (0, 2, new CFClient::UI::Label valign => 0, align => 1, text => "Host:Port"); 674 $table->add (0, 2, new CFClient::UI::Label valign => 0, align => 1, text => "Host:Port");
285 675
286 { 676 {
287 $table->add (1, 2, my $vbox = new CFClient::UI::VBox); 677 $table->add (1, 2, my $vbox = new CFClient::UI::VBox);
288 678
289 $vbox->add (my $HOST = new CFClient::UI::Entry expand => 1, text => $CFG->{host}, connect_changed => sub { 679 $vbox->add (
680 my $HOST = new CFClient::UI::Entry
681 expand => 1,
682 text => $CFG->{host},
683 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
684 connect_changed => sub {
685 my ($self, $value) = @_;
686 $CFG->{host} = $value;
687 }
688 );
689
690 $METASERVER = metaserver_dialog;
691
692 $vbox->add (new CFClient::UI::Flopper
693 expand => 1,
694 text => "Metaserver",
695 other => $METASERVER,
696 tooltip => "Show a list of avaible crossfire servers",
697 connect_open => sub {
698 update_metaserver $HOST;
699 }
700 );
701 }
702
703 $table->add (0, 4, new CFClient::UI::Label valign => 0, align => 1, text => "Username");
704 $table->add (1, 4, new CFClient::UI::Entry
705 text => $CFG->{user},
706 tooltip => "The name of your character on the server",
707 connect_changed => sub {
290 my ($self, $value) = @_; 708 my ($self, $value) = @_;
291 $CFG->{host} = $value;
292 });
293
294 $METASERVER = metaserver_dialog;
295
296 $vbox->add (new CFClient::UI::Flopper expand => 1, text => "Metaserver", other => $METASERVER, connect_open => sub {
297 update_metaserver $HOST;
298 });
299 }
300
301 $table->add (0, 4, new CFClient::UI::Label valign => 0, align => 1, text => "Username");
302 $table->add (1, 4, new CFClient::UI::Entry text => $CFG->{user}, connect_changed => sub {
303 my ($self, $value) = @_;
304 $CFG->{user} = $value; 709 $CFG->{user} = $value;
710 }
305 }); 711 );
306 712
307 $table->add (0, 5, new CFClient::UI::Label valign => 0, align => 1, text => "Password"); 713 $table->add (0, 5, new CFClient::UI::Label valign => 0, align => 1, text => "Password");
308 $table->add (1, 5, new CFClient::UI::Entry text => $CFG->{password}, hidden => 1, connect_changed => sub { 714 $table->add (1, 5, new CFClient::UI::Entry
715 text => $CFG->{password},
716 hidden => 1,
717 tooltip => "The password for your character",
718 connect_changed => sub {
309 my ($self, $value) = @_; 719 my ($self, $value) = @_;
310 $CFG->{password} = $value; 720 $CFG->{password} = $value;
721 }
311 }); 722 );
312
313 $table->add (0, 6, new CFClient::UI::Label valign => 0, align => 1, text => "Def. say cmd");
314 $table->add (1, 6, my $saycmd = new CFClient::UI::Entry text => $CFG->{say_command}, connect_changed => sub {
315 my ($self, $value) = @_;
316 $CFG->{say_command} = $value;
317 });
318 723
319 $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size"); 724 $table->add (0, 7, new CFClient::UI::Label valign => 0, align => 1, text => "Map Size");
320 $table->add (1, 7, new CFClient::UI::Slider 725 $table->add (1, 7, new CFClient::UI::Slider
321 req_w => 100, 726 req_w => 100,
322 range => [$CFG->{mapsize}, 10, 100 + 1, 1], 727 range => [$CFG->{mapsize}, 10, 100 + 1, 1],
728 tooltip => "This is the size of the portion of the map update the server sends you. "
729 ."If you set this to a high value you will be able to see further for example.",
323 connect_changed => sub { 730 connect_changed => sub {
324 my ($self, $value) = @_; 731 my ($self, $value) = @_;
325 732
326 $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 733 $CFG->{mapsize} = $self->{range}[0] = $value = int $value;
327 }, 734 },
328 ); 735 );
329 736
330 $table->add (1, 8, new CFClient::UI::Button expand => 1, align => 0, text => "Login", connect_activate => sub { 737 $table->add (1, 8, $LOGIN_BUTTON = new CFClient::UI::Button
738 expand => 1,
739 align => 0,
740 text => "Login",
741 connect_activate => sub {
742 $CONN ? stop_game
331 start_game; 743 : start_game;
744 },
332 }); 745 );
333 746
334 $dialog 747 $dialog
335} 748}
336 749
337sub message_window { 750sub message_window {
338 my $window = new CFClient::UI::FancyFrame 751 my $window = new CFClient::UI::FancyFrame
752 title => "Messages",
339 border_bg => [1, 1, 1, 0.5], 753 border_bg => [1, 1, 1, 1],
340 bg => [0.3, 0.3, 0.3, 0.8], 754 bg => [0, 0, 0, 0.5],
341 user_w => int $::WIDTH / 3, 755 user_w => int $::WIDTH / 3,
342 user_h => int $::HEIGHT / 5, 756 user_h => int $::HEIGHT / 5,
343 child => (my $vbox = new CFClient::UI::VBox); 757 child => (my $vbox = new CFClient::UI::VBox);
344 758
345 $vbox->add ($LOGVIEW = new CFClient::UI::TextView 759 $vbox->add ($LOGVIEW = new CFClient::UI::TextView
346 expand => 1, 760 expand => 1,
761 font => $FONT_FIXED,
347 fontsize => $::CFG->{log_fontsize}, 762 fontsize => $::CFG->{log_fontsize},
348 ); 763 );
349 764
350 $vbox->add (my $input = new CFClient::UI::Entry 765 $vbox->add (my $input = new CFClient::UI::Entry
351 connect_focus_in => sub { 766 connect_focus_in => sub {
384 }; 799 };
385 800
386 $window 801 $window
387} 802}
388 803
804sub make_inventory_window {
805 my $invwin = new CFClient::UI::FancyFrame user_w => 300, user_h => 300, title => "Inventory";
806 $invwin->add ($INV = new CFClient::UI::Inventory expand => 1);
807 $invwin
808}
809
389sub sdl_init { 810sub sdl_init {
390 CFClient::SDL_Init 811 CFClient::SDL_Init
391 and die "SDL::Init failed!\n"; 812 and die "SDL::Init failed!\n";
392} 813}
393 814
394sub video_init { 815sub video_init {
395 sdl_init; 816 sdl_init;
396 817
818 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
819
397 ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] }; 820 ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
398 $FULLSCREEN = $CFG->{fullscreen}; 821 $FULLSCREEN = $CFG->{fullscreen};
399 $FAST = $CFG->{fast}; 822 $FAST = $CFG->{fast};
400 823
401 CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN 824 CFClient::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN
402 or die "SDL_SetVideoMode failed!\n"; 825 or die "SDL_SetVideoMode failed!\n";
403
404 $SDL_EV = new SDL::Event;
405 $SDL_EV->set_unicode (1);
406 826
407 $SDL_ACTIVE = 1; 827 $SDL_ACTIVE = 1;
408 828
409 $LAST_REFRESH = time - 0.01; 829 $LAST_REFRESH = time - 0.01;
410 830
427 fontsize => 0.8, 847 fontsize => 0.8,
428 markup => "Use <b>Alt-Enter</b> to toggle fullscreen mode"; 848 markup => "Use <b>Alt-Enter</b> to toggle fullscreen mode";
429 $ALT_ENTER_MESSAGE->show; 849 $ALT_ENTER_MESSAGE->show;
430 $ALT_ENTER_MESSAGE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h}); 850 $ALT_ENTER_MESSAGE->move (0, $HEIGHT - $ALT_ENTER_MESSAGE->{h});
431 851
432 $CFClient::UI::ROOT->add ($MAPWIDGET = new CFClient::MapWidget); 852 CFClient::UI::FancyFrame->new (
433 $MAPWIDGET->focus_in; 853 border_bg => [1, 1, 1, 192/255],
854 bg => [1, 1, 1, 0],
855 child => ($MAPMAP = new CFClient::MapWidget::MapMap),
856 )->show;
857
858 $MAPWIDGET = new CFClient::MapWidget;
434 $MAPWIDGET->connect (activate_console => sub { 859 $MAPWIDGET->connect (activate_console => sub {
435 my ($mapwidget, $preset) = @_; 860 my ($mapwidget, $preset) = @_;
436 861
437 if ($CONSOLE) { 862 if ($CONSOLE) {
438 $CONSOLE->{input}->{auto_activated} = 1; 863 $CONSOLE->{input}->{auto_activated} = 1;
441 if ($preset && $CONSOLE->{input}->get_text eq '') { 866 if ($preset && $CONSOLE->{input}->get_text eq '') {
442 $CONSOLE->{input}->set_text ($preset); 867 $CONSOLE->{input}->set_text ($preset);
443 } 868 }
444 } 869 }
445 }); 870 });
871 $MAPWIDGET->show;
872 $MAPWIDGET->focus_in;
446 873
447 $CFClient::UI::ROOT->add ($BUTTONBAR = new CFClient::UI::HBox); 874 $BUTTONBAR = new CFClient::UI::HBox;
448 875
449 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Client Setup", other => client_setup); 876 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Client Setup", other => client_setup);
450 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Server Setup", other => server_setup); 877 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Server Setup", other => server_setup);
451 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window); 878 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Message Window", other => message_window);
879
880 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
881
882 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Stats Window", other => make_stats_window);
883 $BUTTONBAR->add (new CFClient::UI::Flopper text => "Inventory", other => make_inventory_window);
452 884
453 $BUTTONBAR->add (new CFClient::UI::Button text => "Save Config", connect_activate => sub { 885 $BUTTONBAR->add (new CFClient::UI::Button text => "Save Config", connect_activate => sub {
454 CFClient::write_cfg "$Crossfire::VARDIR/pclientrc"; 886 CFClient::write_cfg "$Crossfire::VARDIR/pclientrc";
455 status "Configuration Saved"; 887 status "Configuration Saved";
456 }); 888 });
457 889
890 $BUTTONBAR->show;
891
458 $BUTTONBAR->{children}[1]->emit ("activate"); # pop up server setup 892 $BUTTONBAR->{children}[1]->emit ("activate"); # pop up server setup
459
460 my $tgw = new CFClient::UI::FancyFrame (x => $WIDTH - 300, y => 0);
461 $tgw->add (my $hbox = new CFClient::UI::HBox ());
462
463 $hbox->add (my $hg = new CFClient::UI::VGauge (gauge => 'hp'));
464 $hbox->add (my $mg = new CFClient::UI::VGauge (gauge => 'mana'));
465 $hbox->add (my $gg = new CFClient::UI::VGauge (gauge => 'grace'));
466 $hbox->add (my $fg = new CFClient::UI::VGauge (gauge => 'food'));
467
468 $GAUGES = { food => $fg, mana => $mg, hp => $hg, grace => $gg };
469 $CFClient::UI::ROOT->add ($tgw);
470} 893}
471 894
472sub video_shutdown { 895sub video_shutdown {
473 $CFClient::UI::ROOT->{children} = []; 896 $CFClient::UI::ROOT->{children} = [];
897 undef $CFClient::UI::GRAB;
898 undef $CFClient::UI::HOVER;
474 undef $SDL_ACTIVE; 899 undef $SDL_ACTIVE;
475 undef $SDL_EV;
476} 900}
477 901
902my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
478my $bgmusic;#TODO#hack#d# 903my $bgmusic;#TODO#hack#d#
904
905sub audio_channel_finished {
906 my ($channel) = @_;
907
908 warn "channel $channel finished\n";#d#
909}
910
911sub audio_music_finished {
912 return unless $CFG->{bgm_enable};
913
914 # TODO: hack, do play loop and mood music
915 $bgmusic = new_from_file CFClient::MixMusic CFClient::find_rcfile "music/$bgmusic[0]";
916 $bgmusic->play (0);
917
918 push @bgmusic, shift @bgmusic;
919}
479 920
480sub audio_init { 921sub audio_init {
481 if ($CFG->{audio_enable}) { 922 if ($CFG->{audio_enable}) {
482 if (open my $fh, "<:utf8", CFClient::find_rcfile "sounds/config") { 923 if (open my $fh, "<", CFClient::find_rcfile "sounds/config") {
483 924 $SDL_MIXER = !CFClient::Mix_OpenAudio;
484 $SDL_MIXER = new SDL::Mixer 925 CFClient::Mix_AllocateChannels 8;
485 -rate => 22050, 926 CFClient::MixMusic::volume $CFG->{bgm_volume} * 128;
486 -channels => 1, # mono
487 -size => 512;
488 927
489 $SDL_MIXER->allocate_channels (8); 928 audio_music_finished;
490
491 # TODO: hack, do play loop and mood music
492 if ($CFG->{bgm_enable}) {
493 $bgmusic = new SDL::Music CFClient::find_rcfile "music/game3.ogg";
494 $SDL_MIXER->play_music ($bgmusic, -1);
495 $SDL_MIXER->music_volume ($CFG->{bgm_volume});
496 }
497 929
498 while (<$fh>) { 930 while (<$fh>) {
499 next if /^\s*#/; 931 next if /^\s*#/;
500 next if /^\s*$/; 932 next if /^\s*$/;
501 933
502 my ($file, $volume, $event) = split /\s+/, $_, 3; 934 my ($file, $volume, $event) = split /\s+/, $_, 3;
503 935
504 push @SOUNDS, "$volume,$file"; 936 push @SOUNDS, "$volume,$file";
505 937
506 $AUDIO_CHUNKS{"$volume,$file"} ||= do { 938 $AUDIO_CHUNKS{"$volume,$file"} ||= do {
507 my $chunk = new SDL::Sound CFClient::find_rcfile "sounds/$file"; 939 my $chunk = new_from_file CFClient::MixChunk CFClient::find_rcfile "sounds/$file";
508 $chunk->volume ($volume * 128 / 100); 940 $chunk->volume ($volume * 128 / 100);
509 $chunk 941 $chunk
510 }; 942 };
511 } 943 }
512 } else { 944 } else {
514 } 946 }
515 } 947 }
516} 948}
517 949
518sub audio_shutdown { 950sub audio_shutdown {
951 CFClient::Mix_CloseAudio if $SDL_MIXER;
519 undef $SDL_MIXER; 952 undef $SDL_MIXER;
520 @SOUNDS = (); 953 @SOUNDS = ();
521 %AUDIO_CHUNKS = (); 954 %AUDIO_CHUNKS = ();
522} 955}
523 956
536 $want_refresh = 0; 969 $want_refresh = 0;
537 $can_refresh = 0; 970 $can_refresh = 0;
538 971
539 $CFClient::UI::ROOT->draw; 972 $CFClient::UI::ROOT->draw;
540 973
541 SDL::GLSwapBuffers; 974 CFClient::SDL_GL_SwapBuffers;
542 975
543 $LAST_REFRESH = $NOW; 976 $LAST_REFRESH = $NOW;
544} 977}
545 978
546my $refresh_watcher = Event->timer (after => 0, hard => 1, interval => 1 / $MAX_FPS, cb => sub { 979my $refresh_watcher = Event->timer (after => 0, hard => 1, interval => 1 / $MAX_FPS, cb => sub {
547 $NOW = time; 980 $NOW = time;
548 981
549 ($SDL_CB{$SDL_EV->type} || sub { warn "unhandled event ", $SDL_EV->type })->() 982 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
550 while $SDL_EV->poll; 983 for CFClient::SDL_PollEvent;
551 984
552 if (%animate_object) { 985 if (%animate_object) {
553 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object; 986 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
554 $want_refresh++; 987 $want_refresh++;
555 } 988 }
578@conn::ISA = Crossfire::Protocol::; 1011@conn::ISA = Crossfire::Protocol::;
579 1012
580sub conn::stats_update { 1013sub conn::stats_update {
581 my ($self, $stats) = @_; 1014 my ($self, $stats) = @_;
582 1015
583 # i love text protocols!!! 1016 update_stats_window ($stats);
584 # FIXME: the stats are somehow weird
585 my $hp = $stats->{1};
586 my $hp_m = $stats->{2};
587 my $sp = $stats->{3};
588 my $sp_m = $stats->{4};
589 my $fo = $stats->{18};
590 my $fo_m = 1000;
591 my $gr = $stats->{23};
592 my $gr_m = $stats->{24};
593
594 #d# warn "DATA $hp $hp_m $sp $sp_m $fo $fo_m $gr $gr_m\n";
595 $GAUGES->{hp}->set_value ($hp, $hp_m);
596 $GAUGES->{mana}->set_value ($sp, $sp_m);
597 $GAUGES->{food}->set_value ($fo, $fo_m);
598 $GAUGES->{grace}->set_value ($gr, $gr_m);
599} 1017}
600 1018
601sub conn::user_send { 1019sub conn::user_send {
602 my ($self, $command) = @_; 1020 my ($self, $command) = @_;
603 1021
628 1046
629 my ($hash, $x, $y, $w, $h) = @$map_info; 1047 my ($hash, $x, $y, $w, $h) = @$map_info;
630 1048
631 my $data = $MAP->get_rect ($x, $y, $w, $h); 1049 my $data = $MAP->get_rect ($x, $y, $w, $h);
632 $MAPCACHE->put ($hash => Compress::LZF::compress $data); 1050 $MAPCACHE->put ($hash => Compress::LZF::compress $data);
633
634 warn sprintf "SAVEmap[%s] length %d\n", $hash, length $data;#d# 1051 #warn sprintf "SAVEmap[%s] length %d\n", $hash, length $data;#d#
635
636} 1052}
637 1053
638sub conn::map_clear { 1054sub conn::map_clear {
639 my ($self) = @_; 1055 my ($self) = @_;
640 1056
641 $self->flush_map; 1057 $self->flush_map;
642 delete $self->{neigh}; 1058 delete $self->{neigh_map};
643 1059
644 $MAP->clear; 1060 $MAP->clear;
645} 1061}
646 1062
647 1063
648sub conn::load_map($$$) { 1064sub conn::load_map($$$) {
649 my ($self, $hash, $x, $y) = @_; 1065 my ($self, $hash, $x, $y) = @_;
650 1066
651 if (defined (my $data = $MAPCACHE->get ($hash))) { 1067 if (defined (my $data = $MAPCACHE->get ($hash))) {
652 $data = Compress::LZF::decompress $data; 1068 $data = Compress::LZF::decompress $data;
653 warn sprintf "LOADmap[%s,%d,%d] length %d\n", $hash, $x, $y, length $data;#d# 1069 #warn sprintf "LOADmap[%s,%d,%d] length %d\n", $hash, $x, $y, length $data;#d#
654 for my $id ($MAP->set_rect ($x, $y, $data)) { 1070 for my $id ($MAP->set_rect ($x, $y, $data)) {
655 my $data = $TILECACHE->get ($id) 1071 my $data = $TILECACHE->get ($id)
656 or next; 1072 or next;
657 1073
658 $self->set_texture ($id => $data); 1074 $self->set_texture ($id => $data);
659 } 1075 }
660 } 1076 }
661} 1077}
662 1078
1079# this method does a "flood fill" into every tile direction
1080# it assumes that tiles are arranged in a rectangular grid,
1081# i.e. a map is the same as the left of the right map etc.
1082# failure to comply are harmless and result in display errors
1083# at worst.
663sub conn::flood_fill { 1084sub conn::flood_fill {
664 my ($self, $path, $hash, $flags, $x0, $y0, $x1, $y1) = @_; 1085 my ($self, $gx, $gy, $path, $hash, $flags) = @_;
665 1086
666 # the server does not allow map paths > 6 1087 # the server does not allow map paths > 6
667 return if 6 <= length $path; 1088 return if 7 <= length $path;
668 1089
669 for my $tile (1..4) { 1090 my ($x0, $y0, $x1, $y1) = @{$self->{neigh_rect}};
670 next if $self->{neigh}{$hash}[$tile]; 1091
1092 for (
1093 [1, 0, -1],
1094 [2, 1, 0],
1095 [3, 0, 1],
1096 [4, -1, 0],
1097 ) {
1098 my ($tile, $dx, $dy) = @$_;
1099
1100 my $gx = $gx + $dx;
1101 my $gy = $gy + $dy;
1102
671 next unless $flags & (1 << ($tile - 1)); 1103 next unless $flags & (1 << ($tile - 1));
1104 next if $self->{neigh_grid}{$gx, $gy}++;
672 1105
673 my $neigh = $self->{neigh}{$hash} ||= []; 1106 my $neigh = $self->{neigh_map}{$hash} ||= [];
674 1107 if (my $info = $neigh->[$tile]) {
675 $self->send_mapinfo ("spatial $path$tile", sub {
676 my ($mode, $flags, $x, $y, $w, $h, $hash) = @_; 1108 my ($flags, $x, $y, $w, $h, $hash) = @$info;
677 1109
678 #warn "map<$path>_$tile=<$mode,$x,$y,$w,$h,$hash>\n";#d#
679 return if $mode ne "spatial";
680
681 $x += $MAP->ox;
682 $y += $MAP->oy;
683
684 $self->load_map ($hash, $x, $y)
685 unless $self->{neigh}{$hash}[5]++;#d#
686
687 $neigh->[$tile] = [$x, $y, $w, $h];
688
689 $self->flood_fill ("$path$tile", $hash, $flags, $x0, $y0, $x1, $y1) 1110 $self->flood_fill ($gx, $gy, "$path$tile", $hash, $flags)
690 if $x >= $x0 && $x + $w < $x1 && $y >= $y0 && $y + $h < $y1; 1111 if $x >= $x0 && $x + $w < $x1 && $y >= $y0 && $y + $h < $y1;
1112
1113 } else {
1114 $self->send_mapinfo ("spatial $path$tile", sub {
1115 my ($mode, $flags, $x, $y, $w, $h, $hash) = @_;
1116
1117 return if $mode ne "spatial";
1118
1119 $x += $MAP->ox;
1120 $y += $MAP->oy;
1121
1122 $self->load_map ($hash, $x, $y)
1123 unless $self->{neigh_map}{$hash}[5]++;#d#
1124
1125 $neigh->[$tile] = [$flags, $x, $y, $w, $h, $hash];
1126
1127 $self->flood_fill ($gx, $gy, "$path$tile", $hash, $flags)
1128 if $x >= $x0 && $x + $w < $x1 && $y >= $y0 && $y + $h < $y1;
1129 });
691 }); 1130 }
692 } 1131 }
693} 1132}
694 1133
695sub conn::map_change { 1134sub conn::map_change {
696 my ($self, $mode, $flags, $x, $y, $w, $h, $hash) = @_; 1135 my ($self, $mode, $flags, $x, $y, $w, $h, $hash) = @_;
697 1136
698 $self->flush_map; 1137 $self->flush_map;
699 1138
700 my ($ox, $oy) = ($::MAP->ox, $::MAP->oy); 1139 my ($ox, $oy) = ($::MAP->ox, $::MAP->oy);
701 1140
702 my $mapmapw = 250; 1141 my $mapmapw = $MAPMAP->{w};
703 my $mapmaph = 250; 1142 my $mapmaph = $MAPMAP->{h};
1143
1144 $self->{neigh_rect} = [
1145 $ox - $mapmapw * 0.5, $oy - $mapmapw * 0.5,
1146 $ox + $mapmapw * 0.5 + $w, $oy + $mapmapw * 0.5 + $h,
1147 ];
704 1148
1149 delete $self->{neigh_grid};
705 $self->flood_fill ("", $hash, $flags, 1150 $self->flood_fill (0, 0, "", $hash, $flags);
706 $ox - $mapmapw * 0.5, $oy - $mapmapw * 0.5,
707 $ox + $mapmapw * 0.5, $oy + $mapmapw * 0.5);
708 1151
709 $x += $ox; 1152 $x += $ox;
710 $y += $oy; 1153 $y += $oy;
711 1154
712 $self->{map_info} = [$hash, $x, $y, $w, $h]; 1155 $self->{map_info} = [$hash, $x, $y, $w, $h];
1156
1157 my $map = $self->{map_info}[0];
1158 $map =~ s/^.*?\/([^\/]+)$/\1/;
1159 $STATWIDS->{map}->set_text ("Map: " . $map);
713 1160
714 $self->load_map ($hash, $x, $y); 1161 $self->load_map ($hash, $x, $y);
715} 1162}
716 1163
717sub conn::face_find { 1164sub conn::face_find {
743 } 1190 }
744 1191
745gotid: 1192gotid:
746 $face->{id} = $id; 1193 $face->{id} = $id;
747 $MAP->set_face ($facenum => $id); 1194 $MAP->set_face ($facenum => $id);
1195 $self->{faceid}[$facenum] = $id;#d#
748 $TILECACHE->get ($id) 1196 $TILECACHE->get ($id)
749} 1197}
750 1198
751sub conn::face_update { 1199sub conn::face_update {
752 my ($self, $facenum, $face) = @_; 1200 my ($self, $facenum, $face) = @_;
760 my ($self, $id, $data) = @_; 1208 my ($self, $id, $data) = @_;
761 1209
762 $self->{texture}[$id] ||= do { 1210 $self->{texture}[$id] ||= do {
763 my $tex = 1211 my $tex =
764 new_from_image CFClient::Texture 1212 new_from_image CFClient::Texture
765 $data, minify => 1; 1213 $data, minify => 1, mipmap => 1;
766 1214
767 $MAP->set_texture ($id, @$tex{qw(name w h s t)}, @{$tex->{minified}}); 1215 $MAP->set_texture ($id, @$tex{qw(name w h s t)}, @{$tex->{minified}});
768 $MAPWIDGET->update; 1216 $MAPWIDGET->update;
769 1217
770 $tex 1218 $tex
778 or return; 1226 or return;
779 1227
780 my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]} 1228 my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
781 or return; 1229 or return;
782 1230
783 $SDL_MIXER->play_channel (-1, $chunk); 1231 $chunk->play;
784# warn "sound $x,$y,$soundnum,$type\n";#d# 1232# warn "sound $x,$y,$soundnum,$type\n";#d#
785} 1233}
1234
1235my $LAST_QUERY; # server is stupid, stupid, stupid
786 1236
787sub conn::query { 1237sub conn::query {
788 my ($self, $flags, $prompt) = @_; 1238 my ($self, $flags, $prompt) = @_;
789 1239
790 #TODO, display dialog with relevant information 1240 $prompt = $LAST_QUERY unless length $prompt;
791 warn "<<<<QUERY:$flags:$prompt>>>\n";#d# 1241 $LAST_QUERY = $prompt;
1242
1243 my $dialog = new CFClient::UI::FancyFrame
1244 title => "Query",
1245 child => my $vbox = new CFClient::UI::VBox;
1246
1247 $vbox->add (new CFClient::UI::Label
1248 max_w => $::WIDTH * 0.4,
1249 text => $prompt);
1250
1251 if ($flags & Crossfire::Protocol::CS_QUERY_YESNO) {
1252 $vbox->add (my $hbox = new CFClient::HBox);
1253 $hbox->add (new CFClient::Button
1254 text => "No",
1255 connect_activate => sub {
1256 $self->send ("reply n");
1257 $dialog->destroy;
1258 $MAPWIDGET->focus_in;
1259 }
1260 );
1261 $hbox->add (new CFClient::Button
1262 text => "Yes",
1263 connect_activate => sub {
1264 $self->send ("reply y");
1265 $dialog->destroy;
1266 $MAPWIDGET->focus_in;
1267 },
1268 );
1269
1270 $dialog->focus_in;
1271
1272 } elsif ($flags & Crossfire::Protocol::CS_QUERY_SINGLECHAR) {
1273 $dialog->{tooltip} = "Press a key (click on the entry to make sure it has keyboard focus)";
1274 $vbox->add (my $entry = new CFClient::UI::Entry
1275 connect_changed => sub {
1276 $self->send ("reply $_[1]");
1277 $dialog->destroy;
1278 $MAPWIDGET->focus_in;
1279 },
1280 );
1281
1282 $entry->focus_in;
1283
1284 } else {
1285 $dialog->{tooltip} = "Enter the reply and press return (click on the entry to make sure it has keyboard focus)";
1286
1287 $vbox->add (my $entry = new CFClient::UI::Entry
1288 $flags & Crossfire::Protocol::CS_QUERY_HIDEINPUT ? (hiddenchar => "*") : (),
1289 connect_activate => sub {
1290 $self->send ("reply $_[1]");
1291 $dialog->destroy;
1292 $MAPWIDGET->focus_in;
1293 },
1294 );
1295
1296 $entry->focus_in;
1297 }
1298
1299 $dialog->show;
792} 1300}
793 1301
794sub conn::drawinfo { 1302sub conn::drawinfo {
795 my ($self, $color, $text) = @_; 1303 my ($self, $color, $text) = @_;
796 1304
814} 1322}
815 1323
816sub conn::spell_add { 1324sub conn::spell_add {
817 my ($self, $spell) = @_; 1325 my ($self, $spell) = @_;
818 1326
1327 # TODO
1328 # create a widget dynamically, using spell face (CF::Protocol downloads them)
819 $MAPWIDGET->add_command ("invoke $spell->{name}", $spell->{message}, sub { 1329 $MAPWIDGET->add_command ("invoke $spell->{name}", $spell->{message});
820 });
821 $MAPWIDGET->add_command ("cast $spell->{name}", $spell->{message}, sub { 1330 $MAPWIDGET->add_command ("cast $spell->{name}", $spell->{message});
822 });
823} 1331}
824 1332
825sub conn::spell_delete { 1333sub conn::spell_delete {
826 my ($self, $spell) = @_; 1334 my ($self, $spell) = @_;
827} 1335}
828 1336
829sub conn::addme_success { 1337sub conn::addme_success {
830 my ($self) = @_; 1338 my ($self) = @_;
831 1339
832 for my $skill (values %{$self->{skill_info}}) { 1340 for my $skill (values %{$self->{skill_info}}) {
833 $MAPWIDGET->add_command ("ready_skill $skill", "", sub { 1341 $MAPWIDGET->add_command ("ready_skill $skill", "Ready the skill '$skill'");
1342 $MAPWIDGET->add_command ("use_skill $skill", "Immediately use the skill '$skill'");
1343 }
1344}
1345
1346sub update_floorbox {
1347 $CFClient::UI::ROOT->on_refresh ($FLOORBOX => sub {
1348 $FLOORBOX->clear;
1349 $FLOORBOX->add (new CFClient::UI::Empty expand => 1);
1350
1351 # we basically have to use the same sorting as everybody else
1352 for my $item (@{ $CONN->{container}{0} }) {
1353 $FLOORBOX->add (new CFClient::UI::InventoryItem item => $item);
834 }); 1354 }
835 $MAPWIDGET->add_command ("use_skill $skill", "", sub { 1355 });
1356 refresh;
1357}
1358
1359sub conn::container_add {
1360 my ($self, $id, $items) = @_;
1361
1362 update_floorbox if $id == 0;
1363 if ($self->{player}{tag} == $id) {
1364 $INV->set_items ($self->{container}{$self->{player}{tag}});
1365 }
1366 # $self-<{player}{tag} => player inv
1367 #use PApp::Util; warn PApp::Util::dumpval $self->{container}{$self->{player}{tag}};
1368}
1369
1370sub conn::container_clear {
1371 my ($self, $id) = @_;
1372
1373 update_floorbox if $id == 0;
1374 if ($self->{player}{tag} == $id) {
1375 $INV->set_items ($self->{container}{$id});
1376 }
1377
1378# use PApp::Util; warn PApp::Util::dumpval $self->{container}{0};
1379}
1380
1381sub conn::item_delete {
1382 my ($self, @items) = @_;
1383
1384 for (@items) {
1385 update_floorbox if $_->{container} == 0;
1386 if ($self->{player}{tag} == $_->{container}) {
1387 $INV->set_items ($self->{container}{$_->{container}});
836 }); 1388 }
1389 }
1390}
1391
1392sub conn::item_update {
1393 my ($self, $item) = @_;
1394
1395 update_floorbox if $item->{container} == 0;
1396 if ($self->{player}{tag} == $item->{container}) {
1397 $INV->set_items ($self->{container}{$item->{container}});
837 } 1398 }
838} 1399}
839 1400
840%SDL_CB = ( 1401%SDL_CB = (
841 CFClient::SDL_QUIT => sub { 1402 CFClient::SDL_QUIT => sub {
842 Event::unloop -1; 1403 Event::unloop -1;
843 }, 1404 },
844 CFClient::SDL_VIDEORESIZE => sub { 1405 CFClient::SDL_VIDEORESIZE => sub {
845 }, 1406 },
846 CFClient::SDL_VIDEOEXPOSE => sub { 1407 CFClient::SDL_VIDEOEXPOSE => \&refresh,
847 refresh; 1408 CFClient::SDL_ACTIVEEVENT => sub {
1409# printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d#
848 }, 1410 },
849 CFClient::SDL_KEYDOWN => sub { 1411 CFClient::SDL_KEYDOWN => sub {
850 if ($SDL_EV->key_mod & CFClient::KMOD_ALT && $SDL_EV->key_sym == 13) { 1412 if ($_[0]{mod} & CFClient::KMOD_ALT && $_[0]{sym} == 13) {
851 # alt-enter 1413 # alt-enter
852 video_shutdown; 1414 video_shutdown;
853 $CFG->{fullscreen} = !$CFG->{fullscreen}; 1415 $CFG->{fullscreen} = !$CFG->{fullscreen};
854 video_init; 1416 video_init;
855 } else { 1417 } else {
856 CFClient::UI::feed_sdl_key_down_event ($SDL_EV); 1418 CFClient::UI::feed_sdl_key_down_event ($_[0]);
857 } 1419 }
858 }, 1420 },
859 CFClient::SDL_KEYUP => sub { 1421 CFClient::SDL_KEYUP => \&CFClient::UI::feed_sdl_key_up_event,
860 CFClient::UI::feed_sdl_key_up_event ($SDL_EV); 1422 CFClient::SDL_MOUSEMOTION => \&CFClient::UI::feed_sdl_motion_event,
861 }, 1423 CFClient::SDL_MOUSEBUTTONDOWN => \&CFClient::UI::feed_sdl_button_down_event,
862 CFClient::SDL_MOUSEMOTION => sub { 1424 CFClient::SDL_MOUSEBUTTONUP => \&CFClient::UI::feed_sdl_button_up_event,
863 CFClient::UI::feed_sdl_motion_event ($SDL_EV);
864 },
865 CFClient::SDL_MOUSEBUTTONDOWN => sub {
866 CFClient::UI::feed_sdl_button_down_event ($SDL_EV);
867 },
868 CFClient::SDL_MOUSEBUTTONUP => sub {
869 CFClient::UI::feed_sdl_button_up_event ($SDL_EV);
870 },
871 CFClient::SDL_ACTIVEEVENT => sub { 1425 CFClient::SDL_USEREVENT => sub {
872# printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d# 1426 if ($_[0]{code} == 1) {
1427 audio_channel_finished $_[0]{data1};
1428 } elsif ($_[0]{code} == 0) {
1429 audio_music_finished;
1430 }
873 }, 1431 },
874); 1432);
875 1433
876############################################################################# 1434#############################################################################
877 1435
878$SIG{INT} = $SIG{TERM} = sub { exit }; 1436$SIG{INT} = $SIG{TERM} = sub { exit };
879 1437
1438CFClient::read_cfg "$Crossfire::VARDIR/pclientrc";
1439
880$TILECACHE = CFClient::db_table "tilecache"; 1440$TILECACHE = CFClient::db_table "tilecache";
881$FACEMAP = CFClient::db_table "facemap"; 1441$FACEMAP = CFClient::db_table "facemap";
882
883CFClient::read_cfg "$Crossfire::VARDIR/pclientrc";
884 1442
885my %DEF_CFG = ( 1443my %DEF_CFG = (
886 sdl_mode => 0, 1444 sdl_mode => 0,
887 width => 640, 1445 width => 640,
888 height => 480, 1446 height => 480,
889 fullscreen => 0, 1447 fullscreen => 0,
890 fast => 0, 1448 fast => 0,
1449 map_scale => 0.5,
891 fow_enable => 1, 1450 fow_enable => 1,
892 fow_intensity => 0.45, 1451 fow_intensity => 0.45,
893 fow_smooth => 0, 1452 fow_smooth => 0,
894 gui_fontsize => 1, 1453 gui_fontsize => 1,
895 log_fontsize => 14, 1454 log_fontsize => 1,
1455 gauge_fontsize => 1,
1456 gauge_size => 0.35,
1457 stat_fontsize => 1,
896 mapsize => 100, 1458 mapsize => 100,
897 host => "crossfire.schmorp.de", 1459 host => "crossfire.schmorp.de",
898 say_command => 'say', 1460 say_command => 'say',
899 audio_enable => 1, 1461 audio_enable => 1,
900 bgm_enable => 1, 1462 bgm_enable => 1,
901 bgm_volume => 64, 1463 bgm_volume => 0.25,
902); 1464);
903 1465
904while (my ($k, $v) = each %DEF_CFG) { 1466while (my ($k, $v) = each %DEF_CFG) {
905 $CFG->{$k} = $v unless exists $CFG->{$k}; 1467 $CFG->{$k} = $v unless exists $CFG->{$k};
906} 1468}
914@SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)"; 1476@SDL_MODES or CFClient::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
915 1477
916$CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES; 1478$CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
917 1479
918{ 1480{
919 my @fonts = map CFClient::find_rcfile $_, qw(uifont.ttf uifontb.ttf uifonti.ttf uifontbi.ttf); 1481 my @fonts = map CFClient::find_rcfile "fonts/$_", qw(
1482 DejaVuSans.ttf
1483 DejaVuSansMono.ttf
1484 DejaVuSans-Bold.ttf
1485 DejaVuSansMono-Bold.ttf
1486 DejaVuSans-Oblique.ttf
1487 DejaVuSansMono-Oblique.ttf
1488 DejaVuSans-BoldOblique.ttf
1489 DejaVuSansMono-BoldOblique.ttf
1490 );
920 1491
921 CFClient::add_font $_ for @fonts; 1492 CFClient::add_font $_ for @fonts;
922 CFClient::set_font $fonts[0]; 1493
1494 $FONT_PROP = new_from_file CFClient::Font $fonts[0];
1495 $FONT_FIXED = new_from_file CFClient::Font $fonts[1];
1496
1497 $FONT_PROP->make_default;
923} 1498}
924 1499
925video_init; 1500video_init;
926audio_init; 1501audio_init;
927 1502
928Event::loop; 1503Event::loop;
929 1504
930END { SDL::Quit } 1505END { CFClient::SDL_Quit }
931 1506
1507=head1 pclient - Crossfire+ and Crossfire game client
932 1508
1509Pclient is a Crossfire+ and Crossfire game client.
1510
1511=head2 Features
1512
1513=over 4
1514
1515=item Fullscreen Map
1516
1517PClient can uses a fullscreen map, which greatly enhances how much of the
1518game world you can see.
1519
1520=item Persistent Map Cache (Crossfire+ only)
1521
1522PClient can persistently cache all map data it received from the
1523server. This not only allows it to display an overview map, but also
1524ensures that once-explored areas will be available the next time you want
1525to explore more.
1526
1527=item Hardware acceleration
1528
1529Unlike most Crossfire clients, PClient take advantage of OpenGL hardware
1530acceleration. Most modern graphics cards have difficulties with 2D
1531acceleration, while 3D graphics is accelerated well.
1532
1533=item No arbitrary limits
1534
1535Unlike other Crossfire clients, pclient does not suffer from arbitrary
1536limits (like a fixed amount of face numbers). There are still limits, but
1537they are not arbitrarily low :)
1538
1539=back
1540
1541=head1 USAGE
1542
1543=head2 The Map
1544
1545The map is always displayed in the background, behind all other windows and UI elements.
1546
1547#TODO# middle-click scrolls
1548#
1549# keys:
1550#
1551# a apply
1552# keypad moves, kp_5 applies ranged attack to self
1553
1554Starting to type enters the I<completion mode>. In that mode, you can type
1555abbreviations or commands and have them executed as soon as they match a
1556valid command. This is best explained by a few examples:
1557
1558Typing B<climb> will display a list of commands with I<climb> in their
1559name, such as I<ready_skill climbing> and I<use_skill climbing>.
1560
1561You can abbreviate commands by typing only the first character of every
1562word. For example, typing I<iwor> will likely select I<invoke word of
1563recall>, while I<ccfo> will select I<cast create food>. Likewise, I<rscli>
1564will likely select I<ready_skill climbing> and I<usl> will give you
1565I<use_skill levitation>.
1566
1567=head2 The map overview
1568
1569#TODO#
1570
1571=head2 The Status area in the lower right corner
1572
1573#TODO#
1574
1575=head2 The I<Statistics>/I>Stats> window
1576
1577#TODO#
1578
1579=head1 FAQ
1580
1581=over 4
1582
1583=item The client is very sluggish and slow, what can I do about this?
1584
1585Most likely, you don't have accelerated OpenGL support. Try to find a
1586newer driver, or a driver from your hardware vendor, that features OpenGL
1587support.
1588
1589If this is not an option, the following Setup options reduce the load and
1590will likely make the client playable with sofwtare rendering (it will
1591still be slow, though):
1592
1593=over 4
1594
1595=item B<Video Mode> should be set as low as possible (e.g. 640x480)
1596
1597=item Enable B<Fast & Ugly> mode
1598
1599=item Disable B<Fog of War>
1600
1601=item Increase B<Map Scale>
1602
1603=back
1604
1605=back
1606
1607=head1 AUTHOR
1608
1609Marc Lehmann <crossfire@schmorp.de>, Robin Redeker <elmex@ta-sa.org>
1610
1611
1612

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines