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

Comparing deliantra/Deliantra-Client/bin/cfplus (file contents):
Revision 1.120 by root, Sun Oct 1 14:48:51 2006 UTC vs.
Revision 1.195 by root, Tue Jul 31 01:03:52 2007 UTC

1#!/opt/bin/perl 1#!/opt/bin/perl
2
3my $startup_done = sub { };
4our $PANGO = "1.5.0";
5
6# do splash-screen thingy on win32
7BEGIN {
8 if (%PAR::LibCache && $^O eq "MSWin32") {
9 while (my ($filename, $zip) = each %PAR::LibCache) {
10 $zip->extractMember ("SPLASH.bmp", "$ENV{PAR_TEMP}/SPLASH.bmp");
11 }
12
13 require Win32::GUI::SplashScreen;
14
15 Win32::GUI::SplashScreen::Show (
16 -file => "$ENV{PAR_TEMP}/SPLASH.bmp",
17 );
18
19 $startup_done = sub {
20 Win32::GUI::SplashScreen::Done (1);
21 };
22 }
23}
2 24
3use strict; 25use strict;
4use utf8; 26use utf8;
27
28use Carp 'verbose';
5 29
6# do things only needed for single-binary version (par) 30# do things only needed for single-binary version (par)
7BEGIN { 31BEGIN {
8 if (%PAR::LibCache) { 32 if (%PAR::LibCache) {
9 @INC = grep ref, @INC; # weed out all paths except pars loader refs 33 @INC = grep ref, @INC; # weed out all paths except pars loader refs
10 34
35 my $tmp = $ENV{PAR_TEMP};
36
11 while (my ($filename, $zip) = each %PAR::LibCache) { 37 while (my ($filename, $zip) = each %PAR::LibCache) {
12 for ($zip->memberNames) { 38 for ($zip->memberNames) {
13 next unless /^\/root\/(.*)/; 39 next unless /^root\/(.*)/;
14 $zip->extractMember ($_, "$ENV{PAR_TEMP}/$1") 40 $zip->extractMember ($_, "$tmp/$1")
15 unless -e "$ENV{PAR_TEMP}/$1"; 41 unless -e "$tmp/$1";
16 } 42 }
17 } 43 }
18 44
19 # TODO: pango-rc file, anybody? 45 if ($^O eq "MSWin32") {
46 # relocatable
47 } else {
48 # unix, need to patch pango rc file
49 open my $fh, "<:perlio", "$tmp/usr/lib/pango/$PANGO/module-files.d/libpango1.0-0.modules"
50 or die "$tmp/usr/lib/$PANGO/module-files.d/libpango1.0-0.modules: $!";
51 local $/;
52 my $rc = <$fh>;
53 $rc =~ s/^\//$tmp\//gm; # replace abs paths by relative ones
20 54
21 unshift @INC, $ENV{PAR_TEMP}; 55 mkdir "$tmp/pango-modules";
56 open my $fh, ">:perlio", "$tmp/pango-modules/pango.modules"
57 or die "$tmp/pango-modules/pango.modules: $!";
58 print $fh $rc;
59
60 $ENV{PANGO_RC_FILE} = "$tmp/pango.rc";
61 open my $fh, ">:perlio", $ENV{PANGO_RC_FILE}
62 or die "$ENV{PANGO_RC_FILE}: $!";
63 print $fh "[Pango]\nModuleFiles = $tmp/pango-modules\n";
64 }
65
66 unshift @INC, $tmp;
22 } 67 }
23} 68}
24 69
25# need to do it again because that pile of garbage called PAR nukes it before main 70# need to do it again because that pile of garbage called PAR nukes it before main
26unshift @INC, $ENV{PAR_TEMP} 71unshift @INC, $ENV{PAR_TEMP}
27 if %PAR::LibCache; 72 if %PAR::LibCache;
28 73
29use Time::HiRes 'time'; 74use Time::HiRes 'time';
30use Event; 75use Event;
76use List::Util qw(max min);
31 77
32use Crossfire; 78use Crossfire;
33use Crossfire::Protocol::Constants; 79use Crossfire::Protocol::Constants;
34 80
35use Compress::LZF; 81use Compress::LZF;
36 82
37use CFPlus; 83use CFPlus;
38use CFPlus::OpenGL (); 84use CFPlus::OpenGL ();
39use CFPlus::Protocol; 85use CFPlus::Protocol;
86use CFPlus::DB;
40use CFPlus::UI; 87use CFPlus::UI;
88use CFPlus::UI::Canvas;
89use CFPlus::UI::Inventory;
90use CFPlus::UI::SpellList;
91use CFPlus::UI::MessageWindow;
41use CFPlus::Pod; 92use CFPlus::Pod;
42use CFPlus::BindingEditor;
43use CFPlus::MapWidget; 93use CFPlus::MapWidget;
94use CFPlus::Macro;
44 95
45$SIG{QUIT} = sub { Carp::cluck "QUIT" }; 96$SIG{QUIT} = sub { Carp::cluck "QUIT" };
46$SIG{PIPE} = 'IGNORE'; 97$SIG{PIPE} = 'IGNORE';
47 98
48$Event::Eval = 0; 99$Event::Eval = 1;
49$Event::DIED = sub { 100$Event::DIED = sub {
50 # TODO: display dialog box or so 101 CFPlus::fatal Carp::longmess $_[1]
51 Carp::cluck $_[1];#d#TODO: remove when stable
52 return;#d#
53 CFPlus::fatal ($_[1]);
54}; 102};
55
56our $VERSION = '0.9';
57 103
58my $MAX_FPS = 60; 104my $MAX_FPS = 60;
59my $MIN_FPS = 5; # unused as of yet 105my $MIN_FPS = 5; # unused as of yet
60 106
61our $META_SERVER = "crossfire.real-time.com:13326"; 107our $META_SERVER = "http://metaserver.schmorp.de/current.json";
62 108
63our $LAST_REFRESH; 109our $LAST_REFRESH;
64our $NOW; 110our $NOW;
65 111
66our $CFG; 112our $CFG;
82 128
83our $MAP; 129our $MAP;
84our $MAPMAP; 130our $MAPMAP;
85our $MAPWIDGET; 131our $MAPWIDGET;
86our $BUTTONBAR; 132our $BUTTONBAR;
87our $LOGVIEW;
88our $CONSOLE;
89our $METASERVER; 133our $METASERVER;
90our $LOGIN_BUTTON; 134our $LOGIN_BUTTON;
91our $QUIT_DIALOG; 135our $QUIT_DIALOG;
92our $HOST_ENTRY; 136our $HOST_ENTRY;
93our $FULLSCREEN_ENABLE; 137our $FULLSCREEN_ENABLE;
104 148
105our $INVENTORY_PAGE; 149our $INVENTORY_PAGE;
106our $STATS_PAGE; 150our $STATS_PAGE;
107our $SKILL_PAGE; 151our $SKILL_PAGE;
108our $SPELL_PAGE; 152our $SPELL_PAGE;
153our $SPELL_LIST;
109 154
110our $HELP_WINDOW; 155our $HELP_WINDOW;
111our $MESSAGE_WINDOW; 156our $MESSAGE_WINDOW;
112our $FLOORBOX; 157our $FLOORBOX;
113our $GAUGES; 158our $GAUGES;
115 160
116our $SDL_ACTIVE; 161our $SDL_ACTIVE;
117our %SDL_CB; 162our %SDL_CB;
118 163
119our $SDL_MIXER; 164our $SDL_MIXER;
165our $MUSIC_DEFAULT = "in_a_heartbeat.ogg";
166our @MUSIC_WANT;
167our $MUSIC_START;
168our $MUSIC_PLAYING_DATA;
169our $MUSIC_PLAYING_META;
170our $MUSIC_PLAYER;
171our $MUSIC_RESUME = 30; # resume music when players less than these many seconds before
120our @SOUNDS; # event => file mapping 172our @SOUNDS; # event => file mapping
121our %AUDIO_CHUNKS; # audio files 173our %AUDIO_CHUNK; # audio files
122 174
123our $ALT_ENTER_MESSAGE; 175our $ALT_ENTER_MESSAGE;
124our $STATUSBOX; 176our $STATUSBOX;
125our $DEBUG_STATUS; 177our $DEBUG_STATUS;
126 178
127our $INV; 179our $INV;
128our $INVR; 180our $INVR;
129our $INV_RIGHT_HB; 181our $INV_RIGHT_HB;
130 182
131our $BIND_EDITOR;
132our $BIND_UPD_CB;
133
134our $PICKUP_CFG; 183our $PICKUP_CFG;
184
185#############################################################################
186#TODO: maybe move into own audio module...
187
188sub audio_channel_finished {
189 my ($channel) = @_;
190
191# warn "channel $channel finished\n";#d#
192}
193
194our %AUDIO_PLAY;
195our %AUDIO_CHUNK;
196
197sub audio_sound_push($) {
198 my ($face) = @_;
199
200 if (my $chunk = $AUDIO_CHUNK{$face}) {
201 for (grep $_->[0] >= Event::time, @{(delete $AUDIO_PLAY{$face}) || []}) {
202 my (undef, $dx, $dy, $vol) = @$_;
203
204 my $channel = CFPlus::Channel::find;
205 $channel->volume (128 + $vol);
206 $dx = $dx / 10 * 255;
207 $channel->set_panning ((min 255, 255 - $dx), (min 255, 255 + $dx));
208
209# my $angle = $dx ? : $dx < 0 ?
210# my $distance = -$vol;
211# $channel->set_position ($angle, $distance);
212
213 $chunk->play ($channel);
214 }
215 } else {
216 # sound_meta not set means data is in flight either way
217 my $meta = $CONN->{sound_meta}{$face}
218 or return;
219
220 # fetch from database
221 CFPlus::DB::get res_data => $meta->{name}, sub {
222 my $vol = $meta->{meta}{volume} || 1;
223 my $rwops = new CFPlus::RW $_[0];
224 my $chunk = new CFPlus::MixChunk $rwops;
225 $chunk->volume ($vol * 128);
226 $AUDIO_CHUNK{$face} = $chunk;
227
228 audio_sound_push ($face);
229 };
230 }
231}
232
233sub audio_sound_play {
234 my ($face, $dx, $dy, $vol) = @_;
235
236 $SDL_MIXER
237 or return;
238
239 my $queue = $AUDIO_PLAY{$face} ||= [];
240 push @$queue, [Event::time + 0.2, $dx, $dy, $vol]; # delay sound by max. 0.2s
241 audio_sound_push $face
242 unless @$queue > 1;
243}
244
245sub audio_music_update_volume {
246 return unless $MUSIC_PLAYING_META;
247 my $volume = $MUSIC_PLAYING_META->{meta}{volume} || 1;
248 my $base = $CFG->{bgm_volume};
249 CFPlus::MixMusic::volume $base * $volume * 128;
250}
251
252sub audio_music_set_ambient {
253 my ($songs) = @_;
254
255 my @want =
256 grep $_,
257 map $CONN->{music_meta}{$_},
258 @$songs;
259
260 if (@want) {
261 @MUSIC_WANT = @want;
262 &audio_music_changed ();
263 }
264}
265
266sub audio_music_start {
267 my $meta = $MUSIC_PLAYING_META;
268
269 CFPlus::DB::get res_data => $meta->{name}, sub {
270 return unless $SDL_MIXER;
271
272 # music might have changed...
273 $meta eq $MUSIC_PLAYING_META
274 or return &audio_music_start ();
275
276 audio_music_update_volume;
277
278 $MUSIC_PLAYING_DATA = \$_[0];
279
280 my $rwops = $meta->{path}
281 ? new_from_file CFPlus::RW $meta->{path}
282 : new CFPlus::RW $$MUSIC_PLAYING_DATA;
283
284 $MUSIC_PLAYER = new CFPlus::MixMusic $rwops
285 or ((warn CFPlus::Mix_GetError), return); # pretty fatal error
286
287 my $NOW = time;
288
289 if ($MUSIC_PLAYING_META->{stop_time} > $NOW - $MUSIC_RESUME) {
290 my $pos = $MUSIC_PLAYING_META->{stop_pos};
291 $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
292 $MUSIC_START = time - $pos;
293 } else {
294 $MUSIC_PLAYER->play (0);
295 $MUSIC_START = time;
296 }
297
298 delete $MUSIC_PLAYING_META->{stop_time};
299 delete $MUSIC_PLAYING_META->{stop_pos};
300 }
301}
302
303sub audio_music_changed {
304 return unless $CFG->{bgm_enable};
305 return unless $SDL_MIXER;
306
307 # default MUSIC_WANT == MUSIC_DEFAULT
308 @MUSIC_WANT = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_WANT;
309
310 # if the currently playing song is acceptable, let it continue
311 return if $MUSIC_PLAYING_META
312 && grep $MUSIC_PLAYING_META == $_, @MUSIC_WANT;
313
314 my $NOW = time;
315
316 if ($MUSIC_PLAYING_META) {
317 $MUSIC_PLAYING_META->{stop_time} = $NOW;
318 $MUSIC_PLAYING_META->{stop_pos} = $NOW - $MUSIC_START;
319 CFPlus::MixMusic::fade_out 1000;
320 } else {
321 # sort by stop time, oldest first
322 @MUSIC_WANT = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_WANT;
323
324 # if the most recently-played piece played very recently,
325 # resume it, else choose the oldest piece for rotation.
326 $MUSIC_PLAYING_META =
327 $MUSIC_WANT[-1]{stop_time} > $NOW - $MUSIC_RESUME
328 ? $MUSIC_WANT[-1]
329 : $MUSIC_WANT[0];
330
331 audio_music_start;
332 }
333}
334
335sub audio_music_finished {
336 undef $MUSIC_PLAYER;
337 undef $MUSIC_PLAYING_META;
338 undef $MUSIC_PLAYING_DATA;
339
340 audio_music_changed;
341}
342
343#############################################################################
135 344
136sub status { 345sub status {
137 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]); 346 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
138} 347}
139 348
140sub debug { 349sub debug {
141 $DEBUG_STATUS->set_text ($_[0]); 350 $DEBUG_STATUS->set_text ($_[0]);
351}
352
353sub message {
354 my ($para) = @_;
355 $MESSAGE_WINDOW->message ($para);
142} 356}
143 357
144sub destroy_query_dialog { 358sub destroy_query_dialog {
145 (delete $_[0]{query_dialog})->destroy 359 (delete $_[0]{query_dialog})->destroy
146 if $_[0]{query_dialog}; 360 if $_[0]{query_dialog};
147} 361}
148 362
363# FIXME: a very ugly hack to wait for stat update look below! #d#
364our $QUERY_TIMER; #d#
365
149# server query dialog 366# server query dialog
150sub server_query { 367sub server_query {
151 my ($conn, $flags, $prompt) = @_; 368 my ($conn, $flags, $prompt) = @_;
369
370 # FIXME: a very ugly hack to wait for stat update #d#
371 if ($prompt =~ /roll new stats/ and not $conn->{stat_change_with}) {
372 unless ($QUERY_TIMER) {
373 $QUERY_TIMER =
374 Event->timer (
375 after => 1,
376 cb => sub {
377 server_query ($conn, $flags, $prompt, 1);
378 $QUERY_TIMER = undef
379 }
380 );
381 return;
382 }
383 }
152 384
153 $conn->{query_dialog} = my $dialog = new CFPlus::UI::Toplevel 385 $conn->{query_dialog} = my $dialog = new CFPlus::UI::Toplevel
154 x => "center", 386 x => "center",
155 y => "center", 387 y => "center",
156 title => "Server Query", 388 title => "Server Query",
157 child => my $vbox = new CFPlus::UI::VBox, 389 child => my $vbox = new CFPlus::UI::VBox,
158 ; 390 ;
159 391
160 my @dialog = my $label = new CFPlus::UI::Label 392 my @dialog = my $label = new CFPlus::UI::Label
161 max_w => $::WIDTH * 0.4, 393 max_w => $::WIDTH * 0.8,
162 ellipsise => 0, 394 ellipsise => 0,
163 text => $prompt; 395 text => $prompt;
164 396
165 if ($flags & CS_QUERY_YESNO) { 397 if ($flags & CS_QUERY_YESNO) {
166 push @dialog, my $hbox = new CFPlus::UI::HBox; 398 push @dialog, my $hbox = new CFPlus::UI::HBox;
183 ); 415 );
184 416
185 $dialog->grab_focus; 417 $dialog->grab_focus;
186 418
187 } elsif ($flags & CS_QUERY_SINGLECHAR) { 419 } elsif ($flags & CS_QUERY_SINGLECHAR) {
188 $dialog->{tooltip} = "#charcreation_focus";
189
190 if ($prompt =~ /Now choose a character|Press any key for the next race/i) { 420 if ($prompt =~ /Now choose a character|Press any key for the next race/i) {
191 $MESSAGE_WINDOW->show; 421 $dialog->{tooltip} = "#charcreation_focus";
192 422
193 unshift @dialog, new CFPlus::UI::Label 423 unshift @dialog, new CFPlus::UI::Label
194 max_w => $::WIDTH * 0.4, 424 max_w => $::WIDTH * 0.8,
195 ellipsise => 0, 425 ellipsise => 0,
196 markup => "\nOr use your keyboard and the text entry below:\n"; 426 markup => "\nOr use your keyboard and the text entry below:\n";
197 427
198 unshift @dialog, my $table = new CFPlus::UI::Table; 428 unshift @dialog, my $table = new CFPlus::UI::Table;
199 429
200 $table->add (0, 0, new CFPlus::UI::Button 430 $table->add_at (0, 0, new CFPlus::UI::Button
201 text => "Next Race", 431 text => "Next Race",
202 on_activate => sub { 432 on_activate => sub {
203 $conn->send ("reply n"); 433 $conn->send ("reply n");
204 destroy_query_dialog $conn; 434 destroy_query_dialog $conn;
205 0 435 0
206 }, 436 },
207 ); 437 );
208 $table->add (2, 0, new CFPlus::UI::Button 438 $table->add_at (2, 0, new CFPlus::UI::Button
209 text => "Accept", 439 text => "Accept",
210 on_activate => sub { 440 on_activate => sub {
211 $conn->send ("reply d"); 441 $conn->send ("reply d");
212 destroy_query_dialog $conn; 442 destroy_query_dialog $conn;
213 0 443 0
214 }, 444 },
215 ); 445 );
216 446
447 if ($conn->{chargen_race_description}) {
448 unshift @dialog, new CFPlus::UI::Label
449 max_w => $::WIDTH * 0.8,
450 ellipsise => 0,
451 markup => "<span foreground='#ccccff'>$conn->{chargen_race_description}</span>",
452 ;
453 }
454
455 unshift @dialog, new CFPlus::UI::Face
456 face => $conn->{player}{face},
457 bg => [.2, .2, .2, 1],
458 min_w => 64,
459 min_h => 64,
460 ;
461
462 if ($conn->{chargen_race_title}) {
463 unshift @dialog, new CFPlus::UI::Label
464 allign => 1,
465 ellipsise => 0,
466 markup => "<span foreground='#ccccff' size='large'>Race: $conn->{chargen_race_title}</span>",
467 ;
468 }
469
217 unshift @dialog, new CFPlus::UI::Label 470 unshift @dialog, new CFPlus::UI::Label
218 max_w => $::WIDTH * 0.4, 471 max_w => $::WIDTH * 0.4,
219 ellipsise => 0, 472 ellipsise => 0,
220 markup => (CFPlus::Pod::section_label ui => "chargen_race"), 473 markup => (CFPlus::Pod::section_label ui => "chargen_race"),
221 ; 474 ;
225 $conn->send ("reply $stat"); 478 $conn->send ("reply $stat");
226 destroy_query_dialog $conn; 479 destroy_query_dialog $conn;
227 return; 480 return;
228 } 481 }
229 482
230 $STATS_PAGE->show;
231 $MESSAGE_WINDOW->hide;
232
233 unshift @dialog, new CFPlus::UI::Label 483 unshift @dialog, new CFPlus::UI::Label
234 max_w => $::WIDTH * 0.4, 484 max_w => $::WIDTH * 0.4,
235 ellipsise => 0, 485 ellipsise => 0,
236 markup => "\nOr use your keyboard and the text entry below:\n"; 486 markup => "\nOr use your keyboard and the text entry below:\n";
237 487
238 unshift @dialog, my $table = new CFPlus::UI::Table; 488 unshift @dialog, my $table = new CFPlus::UI::Table;
239 489
240 # left: re-roll 490 # left: re-roll
241 $table->add (0, 0, new CFPlus::UI::Button 491 $table->add_at (0, 0, new CFPlus::UI::Button
242 text => "Roll Again", 492 text => "Roll Again",
243 on_activate => sub { 493 on_activate => sub {
244 $conn->send ("reply y"); 494 $conn->send ("reply y");
245 destroy_query_dialog $conn; 495 destroy_query_dialog $conn;
246 0 496 0
260 [6 => "Pow", "Power ($conn->{stat}{+CS_STAT_POW})"], 510 [6 => "Pow", "Power ($conn->{stat}{+CS_STAT_POW})"],
261 [7 => "Cha", "Charisma ($conn->{stat}{+CS_STAT_CHA})"], 511 [7 => "Cha", "Charisma ($conn->{stat}{+CS_STAT_CHA})"],
262 ], 512 ],
263 ), 1 .. 2; 513 ), 1 .. 2;
264 514
265 $table->add (2, 0, new CFPlus::UI::Button 515 $table->add_at (2, 0, new CFPlus::UI::Button
266 text => "Swap Stats", 516 text => "Swap Stats",
267 on_activate => sub { 517 on_activate => sub {
268 $conn->{stat_change_with} = $sw2->{value}; 518 $conn->{stat_change_with} = $sw2->{value};
269 $conn->send ("reply $sw1->{value}"); 519 $conn->send ("reply $sw1->{value}");
270 destroy_query_dialog $conn; 520 destroy_query_dialog $conn;
271 0 521 0
272 }, 522 },
273 ); 523 );
274 $table->add (2, 1, new CFPlus::UI::HBox children => [$sw1, $sw2]); 524 $table->add_at (2, 1, new CFPlus::UI::HBox children => [$sw1, $sw2]);
275 525
276 # right: accept 526 # right: accept
277 $table->add (4, 0, new CFPlus::UI::Button 527 $table->add_at (4, 0, new CFPlus::UI::Button
278 text => "Accept", 528 text => "Accept",
279 on_activate => sub { 529 on_activate => sub {
280 $conn->send ("reply n"); 530 $conn->send ("reply n");
281 $STATS_PAGE->hide; 531 $STATS_PAGE->hide;
282 destroy_query_dialog $conn; 532 destroy_query_dialog $conn;
345 status "logging in..."; 595 status "logging in...";
346 596
347 $LOGIN_BUTTON->set_text ("Logout"); 597 $LOGIN_BUTTON->set_text ("Logout");
348 $SETUP_DIALOG->hide; 598 $SETUP_DIALOG->hide;
349 599
350 $PROFILE = $CFG->{profile}{default};
351
352 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32; 600 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
353 601
354 my ($host, $port) = split /:/, $PROFILE->{host}; 602 my ($host, $port) = split /:/, $PROFILE->{host};
355 603
356 $MAP = new CFPlus::Map $mapsize, $mapsize; 604 $MAP = new CFPlus::Map;
357 605
358 $CONN = eval { 606 $CONN = eval {
359 new CFPlus::Protocol 607 new CFPlus::Protocol
360 host => $host, 608 host => $host,
361 port => $port || 13327, 609 port => $port || 13327,
362 user => $PROFILE->{user}, 610 user => $PROFILE->{user},
363 pass => $PROFILE->{password}, 611 pass => $PROFILE->{password},
364 mapw => $mapsize, 612 mapw => $mapsize,
365 maph => $mapsize, 613 maph => $mapsize,
366 614
367 client => "cfplus $VERSION $] $^O", 615 client => "cfplus $CFPlus::VERSION $] $^O",
368 616
369 map_widget => $MAPWIDGET, 617 map_widget => $MAPWIDGET,
370 logview => $LOGVIEW,
371 statusbox => $STATUSBOX, 618 statusbox => $STATUSBOX,
372 map => $MAP, 619 map => $MAP,
373 mapmap => $MAPMAP, 620 mapmap => $MAPMAP,
374 query => \&server_query, 621 query => \&server_query,
375 622
376 sound_play => sub { 623 setup_req => {
377 my ($x, $y, $soundnum, $type) = @_; 624 smoothing => $CFG->{map_smoothing}*1,
378
379 $SDL_MIXER
380 or return;
381
382 my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
383 or return;
384
385 $chunk->play;
386 }, 625 },
387 }; 626 };
388 627
389 if ($CONN) { 628 if ($CONN) {
390 CFPlus::lowdelay fileno $CONN->{fh}; 629 CFPlus::lowdelay fileno $CONN->{fh};
399sub stop_game { 638sub stop_game {
400 $LOGIN_BUTTON->set_text ("Login"); 639 $LOGIN_BUTTON->set_text ("Login");
401 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER); 640 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER);
402 $SETUP_DIALOG->show; 641 $SETUP_DIALOG->show;
403 $PL_WINDOW->hide; 642 $PL_WINDOW->hide;
404 $SPELL_PAGE->clear_spells; 643 $SPELL_LIST->clear_spells;
644 $CFPlus::UI::ROOT->emit (stop_game => ! ! $CONN);
645
646 &audio_music_set_ambient ([]);
405 647
406 return unless $CONN; 648 return unless $CONN;
407 649
408 status "connection closed"; 650 status "connection closed";
409 651
410 destroy_query_dialog $CONN; 652 destroy_query_dialog $CONN;
411 $CONN->destroy; 653 $CONN->destroy;
412 $CONN = 0; # false, does not autovivify 654 $CONN = 0; # false, does not autovivify
413 655
414 undef $MAP; 656 undef $MAP;
415 undef $PROFILE;
416} 657}
417 658
418sub graphics_setup { 659sub graphics_setup {
419 my $vbox = new CFPlus::UI::VBox; 660 my $vbox = new CFPlus::UI::VBox;
420 661
421 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]); 662 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]);
422 663
664 my $row = 0;
665
666 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "OpenGL Info");
667 $table->add_at (1, $row++, new CFPlus::UI::Label valign => 0, fontsize => 0.8, text => CFPlus::OpenGL::gl_vendor . ", " . CFPlus::OpenGL::gl_version,
668 can_events => 1,
669 tooltip => "<tt><span size='8192'>" . (CFPlus::OpenGL::gl_extensions) . "</span></tt>");
670
671 my $vidmode_tooltip =
672 "<b>Video Mode.</b> The video mode to use for fullscreen (and the window size for windowed operation). "
673 . "The format is <i>width</i> x <i>height</i> \@ <i>depth-per-channel</i> + <i>alpha-channel</i>.";
674
423 $table->add (0, 0, new CFPlus::UI::Label valign => 0, align => 1, text => "Video Mode"); 675 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Video Mode");
424 $table->add (1, 0, my $hbox = new CFPlus::UI::HBox); 676 $table->add_at (1, $row++, my $hbox = new CFPlus::UI::HBox);
425 677
678 $hbox->add (my $mode_slider = new CFPlus::UI::Slider
426 $hbox->add (my $mode_slider = new CFPlus::UI::Slider force_w => $WIDTH * 0.1, expand => 1, range => [$CFG->{sdl_mode}, 0, $#SDL_MODES, 0, 1]); 679 force_w => $WIDTH * 0.1, expand => 1, range => [$CFG->{sdl_mode}, 0, $#SDL_MODES, 0, 1],
427 $hbox->add (my $mode_label = new CFPlus::UI::Label align => 0, valign => 0, height => 0.8, template => "9999x9999"); 680 tooltip => $vidmode_tooltip);
681 $hbox->add (my $mode_label = new CFPlus::UI::Label
682 align => 0, valign => 0, height => 0.8, template => "9999x9999@9+9",
683 can_events => 1, tooltip => $vidmode_tooltip);
428 684
429 $mode_slider->connect (changed => sub { 685 $mode_slider->connect (changed => sub {
430 my ($self, $value) = @_; 686 my ($self, $value) = @_;
431 687
432 $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value; 688 $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
433 $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]}); 689 $mode_label->set_text (sprintf '%dx%d@%d+%d', @{$SDL_MODES[$value]});
434 }); 690 });
435 $mode_slider->emit (changed => $mode_slider->{range}[0]); 691 $mode_slider->emit (changed => $mode_slider->{range}[0]);
436 692
437 my $row = 1;
438
439 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fullscreen"); 693 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fullscreen");
440 $table->add (1, $row++, $FULLSCREEN_ENABLE = new CFPlus::UI::CheckBox 694 $table->add_at (1, $row++, $FULLSCREEN_ENABLE = new CFPlus::UI::CheckBox
441 state => $CFG->{fullscreen}, 695 state => $CFG->{fullscreen},
442 tooltip => "Bring the client into fullscreen mode.", 696 tooltip => "Bring the client into fullscreen mode.",
443 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 } 697 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 }
444 ); 698 );
445 699
446 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fast & Ugly"); 700 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
447 $table->add (1, $row++, new CFPlus::UI::CheckBox 701 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
448 state => $CFG->{fast}, 702 state => $CFG->{fast},
449 tooltip => "Lower the visual quality considerably to speed up rendering.", 703 tooltip => "Lower the visual quality considerably to speed up rendering.",
450 on_changed => sub { my ($self, $value) = @_; $CFG->{fast} = $value; 0 } 704 on_changed => sub { my ($self, $value) = @_; $CFG->{fast} = $value; 0 }
451 ); 705 );
452 706
707 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
708 $table->add_at (1, $row++, new CFPlus::UI::Slider
709 range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
710 tooltip => "The base font size used by most GUI elements that do not have their own setting.",
711 on_changed => sub { $CFG->{gui_fontsize} = $_[1]; 0 },
712 );
713
714 $table->add_at (1, $row++, new CFPlus::UI::Button
715 expand => 1, align => 0, text => "Apply",
716 tooltip => "Apply the video settings above.",
717 on_activate => sub {
718 video_shutdown ();
719 video_init ();
720 0
721 }
722 );
723
453 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Scale"); 724 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Scale");
454 $table->add (1, $row++, new CFPlus::UI::Slider 725 $table->add_at (1, $row++, new CFPlus::UI::Slider
455 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1], 726 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
456 tooltip => "Enlarge or shrink the displayed map. Changes are instant.", 727 tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
457 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 } 728 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 }
458 ); 729 );
459 730
731 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing");
732 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
733 state => $CFG->{map_smoothing},
734 tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. "
735 . "This increases load on the graphics subsystem and works only with 2.x servers. "
736 . "Changes take effect at next connection only.",
737 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 }
738 );
739
460 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War"); 740 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War");
461 $table->add (1, $row++, new CFPlus::UI::CheckBox 741 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
462 state => $CFG->{fow_enable}, 742 state => $CFG->{fow_enable},
463 tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.", 743 tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.",
464 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 } 744 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 }
465 ); 745 );
466 746
467 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "FoW Intensity"); 747 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "FoW Intensity");
468 $table->add (1, $row++, new CFPlus::UI::Slider 748 $table->add_at (1, $row++, new CFPlus::UI::Slider
469 range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256], 749 range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256],
470 tooltip => "<b>Fog of War Lightness.</b> The higher the intensity, the lighter the Fog-of-War color. Changes are instant.", 750 tooltip => "<b>Fog of War Lightness.</b> The higher the intensity, the lighter the Fog-of-War color. Changes are instant.",
471 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_intensity} = $value; 0 } 751 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_intensity} = $value; 0 }
472 ); 752 );
473 753
474 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "FoW Smooth");
475 $table->add (1, $row++, new CFPlus::UI::CheckBox
476 state => $CFG->{fow_smooth},
477 tooltip => "Smooth the Fog-of-War a bit to make it more realistic. Changes are instant.",
478 on_changed => sub {
479 my ($self, $value) = @_;
480 $CFG->{fow_smooth} = $value;
481 status "Fog of War smoothing requires OpenGL 1.2 or higher" if $CFPlus::OpenGL::GL_VERSION < 1.2;
482 0
483 }
484 );
485
486 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
487 $table->add (1, $row++, new CFPlus::UI::Slider
488 range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
489 tooltip => "The base font size used by most GUI elements that do not have their own setting.",
490 on_changed => sub { $CFG->{gui_fontsize} = $_[1]; 0 },
491 );
492
493 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Message Fontsize"); 754 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Message Fontsize");
494 $table->add (1, $row++, new CFPlus::UI::Slider 755 $table->add_at (1, $row++, new CFPlus::UI::Slider
495 range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1], 756 range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1],
496 tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant.", 757 tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant.",
497 on_changed => sub { $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 }, 758 on_changed => sub { $MESSAGE_WINDOW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 },
498 ); 759 );
499 760
500 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge fontsize"); 761 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
501 $table->add (1, $row++, new CFPlus::UI::Slider 762 $table->add_at (1, $row++, new CFPlus::UI::Slider
502 range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1], 763 range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1],
503 tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.", 764 tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.",
504 on_changed => sub { 765 on_changed => sub {
505 $CFG->{gauge_fontsize} = $_[1]; 766 $CFG->{gauge_fontsize} = $_[1];
506 &set_gauge_window_fontsize; 767 &set_gauge_window_fontsize;
507 0 768 0
508 } 769 }
509 ); 770 );
510 771
511 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge size"); 772 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge size");
512 $table->add (1, $row++, new CFPlus::UI::Slider 773 $table->add_at (1, $row++, new CFPlus::UI::Slider
513 range => [$CFG->{gauge_size}, 0.2, 0.8], 774 range => [$CFG->{gauge_size}, 0.2, 0.8],
514 tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.", 775 tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.",
515 on_changed => sub { 776 on_changed => sub {
516 $CFG->{gauge_size} = $_[1]; 777 $CFG->{gauge_size} = $_[1];
517 $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size}); 778 $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
518 0 779 0
519 } 780 }
520 ); 781 );
521 782
522 $table->add (1, $row++, new CFPlus::UI::Button
523 expand => 1, align => 0, text => "Apply",
524 tooltip => "Apply the video settings",
525 on_activate => sub {
526 video_shutdown ();
527 video_init ();
528 0
529 }
530 );
531
532 $vbox 783 $vbox
533} 784}
534 785
535sub audio_setup { 786sub audio_setup {
536 my $vbox = new CFPlus::UI::VBox; 787 my $vbox = new CFPlus::UI::VBox;
537 788
538 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]); 789 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]);
539 790
540 my $row = 0; 791 my $row = 0;
541 792
542 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable"); 793 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable");
543 $table->add (1, $row++, new CFPlus::UI::CheckBox 794 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
544 state => $CFG->{audio_enable}, 795 state => $CFG->{audio_enable},
545 tooltip => "<b>Master Audio Enable.</b> If enabled, sound effects and music will be played. If disabled, no audio will be used and the soundcard will not be opened.", 796 tooltip => "<b>Master Audio Enable.</b> If enabled, sound effects and music will be played. If disabled, no audio will be used and the soundcard will not be opened.",
546 on_changed => sub { $CFG->{audio_enable} = $_[1]; 0 } 797 on_changed => sub { $CFG->{audio_enable} = $_[1]; 0 }
547 ); 798 );
548# $table->add (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume"); 799# $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume");
549# $table->add (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub { 800# $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
550# $CFG->{effects_volume} = $_[1]; 801# $CFG->{effects_volume} = $_[1];
551# }); 802# });
552 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music"); 803 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music");
553 $table->add (1, $row++, my $hbox = new CFPlus::UI::HBox); 804 $table->add_at (1, $row++, my $hbox = new CFPlus::UI::HBox);
554 $hbox->add (new CFPlus::UI::CheckBox 805 $hbox->add (new CFPlus::UI::CheckBox
555 expand => 1, state => $CFG->{bgm_enable}, 806 expand => 1, state => $CFG->{bgm_enable},
556 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.", 807 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
557 on_changed => sub { $CFG->{bgm_enable} = $_[1]; 0 } 808 on_changed => sub { $CFG->{bgm_enable} = $_[1]; 0 }
558 ); 809 );
559 $hbox->add (new CFPlus::UI::Slider 810 $hbox->add (new CFPlus::UI::Slider
560 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128], 811 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
561 tooltip => "The volume of the background music. Changes are instant.", 812 tooltip => "The volume of the background music. Changes are instant.",
562 on_changed => sub { $CFG->{bgm_volume} = $_[1]; CFPlus::MixMusic::volume $_[1] * 128; 0 } 813 on_changed => sub { $CFG->{bgm_volume} = $_[1]; audio_music_update_volume; 0 }
563 ); 814 );
564 815
565 $table->add (1, $row++, new CFPlus::UI::Button 816 $table->add_at (1, $row++, new CFPlus::UI::Button
566 expand => 1, align => 0, text => "Apply", 817 expand => 1, align => 0, text => "Apply",
567 tooltip => "Apply the audio settings", 818 tooltip => "Apply the audio settings",
568 on_activate => sub { 819 on_activate => sub {
569 audio_shutdown (); 820 audio_shutdown ();
570 audio_init (); 821 audio_init ();
629} 880}
630 881
631sub debug_setup { 882sub debug_setup {
632 my $table = new CFPlus::UI::Table; 883 my $table = new CFPlus::UI::Table;
633 884
634 $table->add (0, 0, new CFPlus::UI::Label text => "Widget Borders"); 885 $table->add_at (0, 0, new CFPlus::UI::Label text => "Widget Borders");
635 $table->add (1, 0, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 1; 0 }); 886 $table->add_at (1, 0, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 1; 0 });
636 $table->add (0, 1, new CFPlus::UI::Label text => "Tooltip Widget Info"); 887 $table->add_at (0, 1, new CFPlus::UI::Label text => "Tooltip Widget Info");
637 $table->add (1, 1, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 2; 0 }); 888 $table->add_at (1, 1, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 2; 0 });
638 $table->add (0, 2, new CFPlus::UI::Label text => "Show FPS"); 889 $table->add_at (0, 2, new CFPlus::UI::Label text => "Show FPS");
639 $table->add (1, 2, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 4; 0 }); 890 $table->add_at (1, 2, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 4; 0 });
640 $table->add (0, 3, new CFPlus::UI::Label text => "Suppress Tooltips"); 891 $table->add_at (0, 3, new CFPlus::UI::Label text => "Suppress Tooltips");
641 $table->add (1, 3, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 }); 892 $table->add_at (1, 3, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 });
893 $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } );
642 894
643 my @default_smooth = (0.05, 0.13, 0.05, 0.13, 0.30, 0.13, 0.05, 0.13, 0.05); 895 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d#
644 896
645 for my $x (0..2) { 897 $table->add_at (7,7, my $t = new CFPlus::UI::Table expand => 0);
646 for my $y (0 .. 2) { 898 $t->add_at (0,0, new CFPlus::UI::Label text => "a a a a", rowspan => 1, colspan => 2);
647 $table->add ($x + 3, $y, 899 $t->add_at (2,0, new CFPlus::UI::Label text => "b\nb", rowspan => 2, colspan => 1);
648 new CFPlus::UI::Entry 900 $t->add_at (1,2, new CFPlus::UI::Label text => "c c c c", rowspan => 1, colspan => 2);
649 text => $default_smooth[$x * 3 + $y], 901 $t->add_at (0,1, new CFPlus::UI::Label text => "d\nd", rowspan => 2, colspan => 1);
650 on_changed => sub { $MAP->{smooth_matrix}[$x * 3 + $y] = $_[1] if $MAP; 0 }, 902 $t->add_at (1,1, new CFPlus::UI::Label text => "e");
651 ); 903
652 } 904 $table->add_at (7, 6, my $c = new CFPlus::UI::Canvas);
905
906 $c->add_items ({
907 type => "line_loop",
908 color => [0, 1, 0],
909 width => 9,
910 coord_mode => "abs",
911 coord => [[10, 5], [5, 50], [20, 5], [5, 60]],
653 } 912 });
654 913
914 $c->add_items ({
915 type => "lines",
916 color => [1, 1, 0],
917 width => 2,
918 coord_mode => "rel",
919 coord => [[0,0], [1,1], [1,0], [0,1]],
920 });
921
922 $c->add_items ({
923 type => "polygon",
924 color => [0, 0.43, 0],
925 width => 2,
926 coord_mode => "rel",
927 coord => [[0,0.2], [1,.4], [1,.6], [0,.8]],
928 });
655 929
656 $table 930 $table
657} 931}
658 932
659sub stats_window { 933sub stats_window {
707 [2, 4, st_spd => "Spd", 10.54], 981 [2, 4, st_spd => "Spd", 10.54],
708 [2, 5, st_wspd => "WSp", 10.54], 982 [2, 5, st_wspd => "WSp", 10.54],
709 ) { 983 ) {
710 my ($col, $row, $id, $label, $template) = @$_; 984 my ($col, $row, $id, $label, $template) = @$_;
711 985
712 $tbl->add ($col , $row, $STATWIDS->{$id} = new CFPlus::UI::Label 986 $tbl->add_at ($col , $row, $STATWIDS->{$id} = new CFPlus::UI::Label
713 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0, 987 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0,
714 align => +1, template => $template, tooltip => "#stat_$label"); 988 align => +1, template => $template, tooltip => "#stat_$label");
715 $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFPlus::UI::Label 989 $tbl->add_at ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFPlus::UI::Label
716 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0, 990 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0,
717 align => -1, text => $label, tooltip => "#stat_$label"); 991 align => -1, text => $label, tooltip => "#stat_$label");
718 } 992 }
719 993
720 $vb->add (new CFPlus::UI::FancyFrame 994 $vb->add (new CFPlus::UI::FancyFrame
761 cold => ["Cold", 1035 cold => ["Cold",
762 "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)"], 1036 "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)"],
763 ghit => ["Ghost hit", 1037 ghit => ["Ghost hit",
764 "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)"], 1038 "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)"],
765 ); 1039 );
1040
766 for (qw/slow holyw conf fire depl magic 1041 for (qw/slow holyw conf fire depl magic
767 drain acid pois para deat phys 1042 drain acid pois para deat phys
768 blind fear tund elec cold ghit/) 1043 blind fear tund elec cold ghit/)
769 { 1044 {
770 $tbl2->add ($col, $row, 1045 $tbl2->add_at ($col, $row,
771 $STATWIDS->{"res_$_"} = 1046 $STATWIDS->{"res_$_"} =
772 new CFPlus::UI::Label 1047 new CFPlus::UI::Label
773 font => $FONT_FIXED, 1048 font => $FONT_FIXED,
774 template => "-100%", 1049 template => "-100%",
775 align => +1, 1050 align => +1,
776 valign => 0, 1051 valign => 0,
777 can_events => 1, 1052 can_events => 1,
778 can_hover => 1, 1053 can_hover => 1,
779 tooltip => $resist_names{$_}->[1], 1054 tooltip => $resist_names{$_}->[1],
780 ); 1055 );
781 $tbl2->add ($col + 1, $row, new CFPlus::UI::Image 1056 $tbl2->add_at ($col + 1, $row, new CFPlus::UI::Image
782 font => $FONT_FIXED, 1057 font => $FONT_FIXED,
783 can_hover => 1, 1058 can_hover => 1,
784 can_events => 1, 1059 can_events => 1,
785 path => "ui/resist/resist_$_.png", 1060 path => "ui/resist/resist_$_.png",
786 tooltip => $resist_names{$_}->[1], 1061 tooltip => $resist_names{$_}->[1],
787 ); 1062 );
788 $tbl2->add ($col + 2, $row, new CFPlus::UI::Label 1063 $tbl2->add_at ($col + 2, $row, new CFPlus::UI::Label
789 text => $resist_names{$_}->[0], 1064 text => $resist_names{$_}->[0],
790 font => $FONT_FIXED, 1065 font => $FONT_FIXED,
791 can_hover => 1, 1066 can_hover => 1,
792 can_events => 1, 1067 can_events => 1,
793 tooltip => $resist_names{$_}->[1], 1068 tooltip => $resist_names{$_}->[1],
826 return if $METASERVER_ATIME > time; 1101 return if $METASERVER_ATIME > time;
827 $METASERVER_ATIME = time + 60; 1102 $METASERVER_ATIME = time + 60;
828 1103
829 my $table = $METASERVER->{table}; 1104 my $table = $METASERVER->{table};
830 $table->clear; 1105 $table->clear;
831 $table->add (0, 0, my $label = new CFPlus::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list..."); 1106 $table->add_at (0, 0, my $label = new CFPlus::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
832 1107
833 my $buf; 1108 my $ok = 0;
834 1109
835 my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0; 1110 CFPlus::background {
1111 my $ua = CFPlus::lwp_useragent;
836 1112
837 unless ($fh) { 1113 CFPlus::background_msg CFPlus::from_json +(CFPlus::lwp_check $ua->get ($META_SERVER))->decoded_content;
838 $label->set_text ("unable to contact metaserver: $!"); 1114 } sub {
839 return; 1115 my ($msg) = @_;
840 } 1116 if ($msg) {
841
842 Event->io (fd => $fh, poll => 'r', cb => sub {
843 my $res = sysread $fh, $buf, 8192, length $buf;
844
845 if (!defined $res) {
846 $_[0]->w->cancel;
847 $label->set_text ("error while retrieving server list: $!");
848 } elsif ($res == 0) {
849 $_[0]->w->cancel;
850 status "server list retrieved";
851
852 utf8::decode $buf if utf8::valid $buf;
853
854 $table->clear; 1117 $table->clear;
855 1118
856 my @tip = ( 1119 my @tip = (
857 "The current number of users logged in on the server.", 1120 "The current number of users logged in on the server.",
858 "The hostname of the server.", 1121 "The hostname of the server.",
859 "The time this server has been running without being restarted.", 1122 "The time this server has been running without being restarted.",
860 "The server software version - a '+' indicates a Crossfire+ server.", 1123 "The server software version - a '+' indicates a Crossfire+ server.",
861 "Short information about this server provided by its admins.", 1124 "Short information about this server provided by its admins.",
862 ); 1125 );
863 my @col = qw(#Users Host Uptime Version Description); 1126 my @col = qw(#Users Host Uptime Version Description);
864 $table->add ($_, 0, new CFPlus::UI::Label 1127 $table->add_at ($_, 0, new CFPlus::UI::Label
865 can_hover => 1, can_events => 1, 1128 can_hover => 1, can_events => 1,
866 align => 0, fg => [1, 1, 0], 1129 align => 0, fg => [1, 1, 0],
867 text => $col[$_], tooltip => $tip[$_]) 1130 text => $col[$_], tooltip => $tip[$_])
868 for 0 .. $#col; 1131 for 0 .. $#col;
869 1132
870 my @align = qw(1 0 1 1 -1); 1133 my @align = qw(1 0 1 1 -1);
871 1134
872 my $y = 0; 1135 my $y = 0;
873 for my $m (sort { $b->[3] <=> $a->[3] } map [split /\|/], split /\015?\012/, $buf) { 1136 for my $m (@{ $msg->{servers} }) {
874 my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) = @$m; 1137 my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime, $highlight) =
1138 @$m{qw(ip age hostname users version description ibytes obytes uptime highlight)};
875 1139
876 for ($desc) { 1140 for ($desc) {
877 s/<br>/\n/gi; 1141 s/<br>/\n/gi;
878 s/<li>/\n· /gi; 1142 s/<li>/\n· /gi;
879 s/<.*?>//sgi; 1143 s/<.*?>//sgi;
880 s/&/&amp;/g; 1144 s/&amp;/&/g;
881 s/</&lt;/g; 1145 s/&lt;/</g;
882 s/>/&gt;/g; 1146 s/&gt;/>/g;
883 } 1147 }
884 1148
885 $uptime = sprintf "%dd %02d:%02d:%02d", 1149 $uptime = sprintf "%dd %02d:%02d:%02d",
886 (int $m->[8] / 86400), 1150 (int $uptime / 86400),
887 (int $m->[8] / 3600) % 24, 1151 (int $uptime / 3600) % 24,
888 (int $m->[8] / 60) % 60, 1152 (int $uptime / 60) % 60,
889 $m->[8] % 60; 1153 $uptime % 60;
890 1154
891 $m = [$users, $host, $uptime, $version, $desc]; 1155 $m = [$users, $host, $uptime, $version, $desc];
892 1156
893 $y++; 1157 $y++;
894 1158
895 $table->add (scalar @$m, $y, new CFPlus::UI::VBox children => [ 1159 $table->add_at (scalar @$m, $y, new CFPlus::UI::VBox children => [
896 (new CFPlus::UI::Button 1160 (new CFPlus::UI::Button
897 text => "Use", 1161 text => "Use",
898 tooltip => "Put this server into the <b>Host:Port</b> field", 1162 tooltip => "Put this server into the <b>Host:Port</b> field",
899 on_activate => sub { 1163 on_activate => sub {
900 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host); 1164 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host);
903 }, 1167 },
904 ), 1168 ),
905 (new CFPlus::UI::Empty expand => 1), 1169 (new CFPlus::UI::Empty expand => 1),
906 ]); 1170 ]);
907 1171
908 $table->add ($_, $y, new CFPlus::UI::Label 1172 $table->add_at ($_, $y, new CFPlus::UI::Label
1173 max_w => $::WIDTH * 0.4,
909 ellipsise => 0, 1174 ellipsise => 0,
910 align => $align[$_], 1175 align => $align[$_],
911 text => $m->[$_], 1176 text => $m->[$_],
912 tooltip => $tip[$_], 1177 tooltip => $tip[$_],
1178 fg => ($highlight ? [1, 1, 1] : [.7, .7, .7]),
913 can_hover => 1, 1179 can_hover => 1,
914 can_events => 1, 1180 can_events => 1,
915 fontsize => 0.8) 1181 fontsize => 0.8)
916 for 0 .. $#$m; 1182 for 0 .. $#$m;
917 } 1183 }
1184 } else {
1185 $ok or $label->set_text ("error while contacting metaserver");
918 } 1186 }
919 }); 1187 };
1188
920} 1189}
921 1190
922sub metaserver_dialog { 1191sub metaserver_dialog {
923 my $vbox = new CFPlus::UI::VBox; 1192 my $vbox = new CFPlus::UI::VBox;
924 my $table = new CFPlus::UI::Table; 1193 my $table = new CFPlus::UI::Table;
928 title => "Server List", 1197 title => "Server List",
929 name => 'metaserver_dialog', 1198 name => 'metaserver_dialog',
930 x => 'center', 1199 x => 'center',
931 y => 'center', 1200 y => 'center',
932 z => 3, 1201 z => 3,
1202 force_w => $::WIDTH * 0.9,
933 force_h => $::HEIGHT * 0.4, 1203 force_h => $::HEIGHT * 0.7,
934 child => $vbox, 1204 child => $vbox,
935 has_close_button => 1, 1205 has_close_button => 1,
936 table => $table, 1206 table => $table,
937 on_visibility_change => sub { 1207 on_visibility_change => sub {
938 update_metaserver ($_[0]) if $_[1]; 1208 update_metaserver ($_[0]) if $_[1];
948 1218
949 $vbox->add (new CFPlus::UI::FancyFrame 1219 $vbox->add (new CFPlus::UI::FancyFrame
950 label => "Connection Settings", 1220 label => "Connection Settings",
951 child => (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]), 1221 child => (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]),
952 ); 1222 );
953 $table->add (0, 2, new CFPlus::UI::Label valign => 0, align => 1, text => "Host:Port"); 1223 $table->add_at (0, 2, new CFPlus::UI::Label valign => 0, align => 1, text => "Host:Port");
954 1224
955 { 1225 {
956 $table->add (1, 2, my $vbox = new CFPlus::UI::VBox); 1226 $table->add_at (1, 2, my $vbox = new CFPlus::UI::VBox);
957 1227
958 $vbox->add ( 1228 $vbox->add (
959 $HOST_ENTRY = new CFPlus::UI::Entry 1229 $HOST_ENTRY = new CFPlus::UI::Entry
960 expand => 1, 1230 expand => 1,
961 text => $CFG->{profile}{default}{host}, 1231 text => $CFG->{profile}{default}{host},
962 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to", 1232 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
963 on_changed => sub { 1233 on_changed => sub {
964 my ($self, $value) = @_; 1234 my ($self, $value) = @_;
965 $CFG->{profile}{default}{host} = $value; 1235 $CFG->{profile}{default}{host} = $value;
966 0 1236 1
967 } 1237 }
968 ); 1238 );
969 1239
970 $vbox->add (new CFPlus::UI::Button 1240 $vbox->add (new CFPlus::UI::Button
971 expand => 1, 1241 expand => 1,
972 text => "Server List", 1242 text => "Server List",
973 other => $METASERVER, 1243 other => $METASERVER,
974 tooltip => "Show a list of available crossfire servers", 1244 tooltip => "Show a list of available crossfire servers",
975 on_activate => sub { $METASERVER->toggle_visibility; 0 }, 1245 on_activate => sub { $METASERVER->toggle_visibility; 0 },
976 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 0 }, 1246 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 1 },
977 ); 1247 );
978 } 1248 }
979 1249
980 $table->add (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username"); 1250 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username");
981 $table->add (1, 4, new CFPlus::UI::Entry 1251 $table->add_at (1, 4, new CFPlus::UI::Entry
982 text => $CFG->{profile}{default}{user}, 1252 text => $CFG->{profile}{default}{user},
983 tooltip => "The name of your character on the server", 1253 tooltip => "The name of your character on the server",
984 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value } 1254 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value; 1 }
985 ); 1255 );
986 1256
987 $table->add (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password"); 1257 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password");
988 $table->add (1, 5, new CFPlus::UI::Entry 1258 $table->add_at (1, 5, new CFPlus::UI::Entry
989 text => $CFG->{profile}{default}{password}, 1259 text => $CFG->{profile}{default}{password},
990 hidden => 1, 1260 hidden => 1,
991 tooltip => "The password for your character", 1261 tooltip => "The password for your character",
992 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value } 1262 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value; 1 }
993 ); 1263 );
994 1264
995 $table->add (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size"); 1265 $table->add_at (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size");
996 $table->add (1, 7, new CFPlus::UI::Slider 1266 $table->add_at (1, 7, new CFPlus::UI::Slider
997 force_w => 100, 1267 force_w => 100,
998 range => [$CFG->{mapsize}, 10, 100, 0, 1], 1268 range => [$CFG->{mapsize}, 10, 100, 0, 1],
999 tooltip => "This is the size of the portion of the map update the server sends you. " 1269 tooltip => "This is the size of the portion of the map update the server sends you. "
1000 . "If you set this to a high value you will be able to see further, " 1270 . "If you set this to a high value you will be able to see further, "
1001 . "but you also increase bandwidth requirements and latency. " 1271 . "but you also increase bandwidth requirements and latency. "
1002 . "This option is only used once at log-in.", 1272 . "This option is only used once at log-in.",
1003 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 0 }, 1273 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 1 },
1004 ); 1274 );
1005 1275
1006 $table->add (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Face Prefetch"); 1276 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate");
1007 $table->add (1, 8, new CFPlus::UI::CheckBox 1277 $table->add_at (1, 8, new CFPlus::UI::Entry
1008 state => $CFG->{face_prefetch}, 1278 text => $CFG->{output_rate},
1009 tooltip => "<b>Background Image Prefetch</b>\n\n" 1279 tooltip => "The maximum bandwidth in bytes per second that the server should not exceed "
1010 . "If enabled, the client automatically pre-fetches images from the server. " 1280 . "when sending data. When 0 or unset, the server "
1011 . "This might increase or create lag, but increases the chances " 1281 . "default will be used, which is usually around 100kb/s. Most servers will "
1012 . "of faces being ready for display when you encounter them. " 1282 . "dynamically find an optimal rate, so adjust this only when necessary.",
1013 . "It also uses up server bandwidth on every connect, "
1014 . "so only set it if you really need to prefetch images. "
1015 . "This option can be set and unset any time.",
1016 on_changed => sub { $CFG->{face_prefetch} = $_[1]; 0 }, 1283 on_changed => sub { $CFG->{output_rate} = $_[1]; 1 },
1017 ); 1284 );
1018 1285
1019 $table->add (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count"); 1286 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1020 $table->add (1, 9, new CFPlus::UI::Entry 1287 $table->add_at (1, 9, new CFPlus::UI::Entry
1021 text => $CFG->{output_count}, 1288 text => $CFG->{output_count},
1022 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.", 1289 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1023 on_changed => sub { $CFG->{output_count} = $_[1]; 0 }, 1290 on_changed => sub { $CFG->{output_count} = $_[1]; 1 },
1024 ); 1291 );
1025 1292
1026 $table->add (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync"); 1293 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync");
1027 $table->add (1, 10, new CFPlus::UI::Entry 1294 $table->add_at (1, 10, new CFPlus::UI::Entry
1028 text => $CFG->{output_sync}, 1295 text => $CFG->{output_sync},
1029 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.", 1296 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1030 on_changed => sub { $CFG->{output_sync} = $_[1]; 0 }, 1297 on_changed => sub { $CFG->{output_sync} = $_[1]; 1 },
1031 ); 1298 );
1032 1299
1033 $table->add (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button 1300 $table->add_at (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button
1034 expand => 1, 1301 expand => 1,
1035 align => 0, 1302 align => 0,
1036 text => "Login", 1303 text => "Login",
1037 on_activate => sub { 1304 on_activate => sub {
1038 $CONN ? stop_game 1305 $CONN ? stop_game
1039 : start_game; 1306 : start_game;
1040 0 1307 1
1041 }, 1308 },
1042 ); 1309 );
1043 1310
1044 $table->add (0, 12, new CFPlus::UI::Label valign => 0, align => 1, text => "Chat Command"); 1311 $vbox->add (new CFPlus::UI::FancyFrame
1045 $table->add (1, 12, my $saycmd = new CFPlus::UI::Entry 1312 label => "Server Info",
1046 text => $CFG->{say_command}, 1313 child => ($SERVER_INFO = new CFPlus::UI::Label ellipsise => 0),
1047 tooltip => "This is the command that will be used if you write a line in the message window entry or press <b>\"</b> in the map window. "
1048 . "Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
1049 . "But you could also set it to <b>tell <i>playername</i></b> to only chat with that user.",
1050 on_changed => sub {
1051 my ($self, $value) = @_;
1052 $CFG->{say_command} = $value;
1053 0
1054 }
1055 ); 1314 );
1056 1315
1316 $vbox
1317}
1318
1319sub client_setup {
1320 my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1];
1321
1322 my $row = 0;
1323
1057 $table->add (0, 13, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day"); 1324 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day");
1058 $table->add (1, 13, my $saycmd = new CFPlus::UI::CheckBox 1325 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
1059 state => $CFG->{show_tips}, 1326 state => $CFG->{show_tips},
1060 tooltip => "Show the <b>Tip of the day</b> window at startup?", 1327 tooltip => "Show the <b>Tip of the day</b> window at startup?",
1061 on_changed => sub { 1328 on_changed => sub {
1062 my ($self, $value) = @_; 1329 my ($self, $value) = @_;
1063 $CFG->{shop_tips} = $value; 1330 $CFG->{show_tips} = $value;
1064 0 1331 0
1065 } 1332 }
1066 ); 1333 );
1067 1334
1068 $vbox->add (new CFPlus::UI::FancyFrame 1335 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Messages Window Size");
1069 label => "Server Info", 1336 $table->add_at (1, $row++, my $saycmd = new CFPlus::UI::Entry
1070 child => ($SERVER_INFO = new CFPlus::UI::Label ellipsise => 0), 1337 text => $CFG->{logview_max_par},
1071 ); 1338 tooltip => "This is maximum number of messages remembered in the <b>Messages</b> window. If the server "
1072 1339 . "sends more messages than this number, older messages get removed to save memory and "
1073 $vbox 1340 . "computing time. A value of <b>0</b> disables this feature, but that is not recommended.",
1074}
1075
1076sub message_window {
1077 my $window = new CFPlus::UI::Toplevel
1078 name => "message_window",
1079 title => "Messages",
1080 border_bg => [1, 1, 1, 1],
1081 x => "max",
1082 y => 0,
1083 force_w => $::WIDTH * 0.4,
1084 force_h => $::HEIGHT * 0.5,
1085 child => (my $vbox = new CFPlus::UI::VBox),
1086 has_close_button => 1;
1087
1088 $vbox->add ($LOGVIEW);
1089
1090 $vbox->add (my $input = new CFPlus::UI::Entry
1091 tooltip => "<b>Chat Box</b>. If you enter a text and press return/enter here, the current <i>communication command</i> "
1092 . "from the client setup will be prepended (e.g. <b>shout</b>, <b>chat</b>...). "
1093 . "If you prepend a slash (/), you will submit a command instead (similar to IRC). "
1094 . "A better way to submit commands (and the occasional chat command) is often the map command completer.",
1095 on_focus_in => sub { 1341 on_changed => sub {
1096 my ($input, $prev_focus) = @_; 1342 my ($self, $value) = @_;
1097 1343 $MESSAGE_WINDOW->set_max_para ($CFG->{logview_max_par} = $value*1);
1098 delete $input->{refocus_map};
1099
1100 if ($prev_focus == $MAPWIDGET && $input->{auto_activated}) {
1101 $input->{refocus_map} = 1;
1102 }
1103 delete $input->{auto_activated};
1104
1105 0 1344 0
1106 }, 1345 },
1107 on_activate => sub {
1108 my ($input, $text) = @_;
1109 $input->set_text ('');
1110
1111 if ($text =~ /^\/(.*)/) {
1112 $::CONN->user_send ($1);
1113 } else {
1114 my $say_cmd = $::CFG->{say_command} || 'say';
1115 $::CONN->user_send ("$say_cmd $text");
1116 }
1117 if ($input->{refocus_map}) {
1118 delete $input->{refocus_map};
1119 $MAPWIDGET->focus_in
1120 }
1121
1122 0
1123 },
1124 on_escape => sub {
1125 $MAPWIDGET->grab_focus;
1126
1127 0
1128 },
1129 );
1130
1131 $CONSOLE = {
1132 window => $window,
1133 input => $input,
1134 }; 1346 );
1135 1347
1136 $window 1348 $table
1137} 1349}
1138 1350
1139sub autopickup_setup { 1351sub autopickup_setup {
1352 my $r = new CFPlus::UI::ScrolledWindow (
1353 expand => 1,
1354 scroll_y => 1
1355 );
1140 my $table = new CFPlus::UI::Table; 1356 $r->add (my $table = new CFPlus::UI::Table
1357 row_expand => [0],
1358 col_expand => [0, 1, 0, 1],
1359 );
1141 1360
1142 for ( 1361 for (
1143 ["General", 0, 0, 1362 ["General", 0, 0,
1144 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE], 1363 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE],
1145 ["Inhibit autopickup" => PICKUP_INHIBIT], 1364 ["Inhibit autopickup" => PICKUP_INHIBIT],
1159 ["Boots" => PICKUP_BOOTS], 1378 ["Boots" => PICKUP_BOOTS],
1160 ["Gloves" => PICKUP_GLOVES], 1379 ["Gloves" => PICKUP_GLOVES],
1161 ["Cloaks" => PICKUP_CLOAK], 1380 ["Cloaks" => PICKUP_CLOAK],
1162 ], 1381 ],
1163 1382
1164 ["Readables", 2, 2, 1383 ["Readables", 2, 0,
1165 ["Spellbooks" => PICKUP_SPELLBOOK], 1384 ["Spellbooks" => PICKUP_SPELLBOOK],
1166 ["Skillscrolls" => PICKUP_SKILLSCROLL], 1385 ["Skillscrolls" => PICKUP_SKILLSCROLL],
1167 ["Normal Books/Scrolls" => PICKUP_READABLES], 1386 ["Normal Books/Scrolls" => PICKUP_READABLES],
1168 ], 1387 ],
1169 ["Misc", 2, 7, 1388 ["Misc", 2, 5,
1170 ["Food" => PICKUP_FOOD], 1389 ["Food" => PICKUP_FOOD],
1171 ["Drinks" => PICKUP_DRINK], 1390 ["Drinks" => PICKUP_DRINK],
1172 ["Valuables (Money, Gems)" => PICKUP_VALUABLES], 1391 ["Valuables (Money, Gems)" => PICKUP_VALUABLES],
1173 ["Keys" => PICKUP_KEY], 1392 ["Keys" => PICKUP_KEY],
1174 ["Magical Items" => PICKUP_MAGICAL], 1393 ["Magical Items" => PICKUP_MAGICAL],
1175 ["Potions" => PICKUP_POTION], 1394 ["Potions" => PICKUP_POTION],
1176 ["Magic Devices" => PICKUP_MAGIC_DEVICE], 1395 ["Magic Devices" => PICKUP_MAGIC_DEVICE],
1177 ["Ignore cursed" => PICKUP_NOT_CURSED], 1396 ["Ignore cursed" => PICKUP_NOT_CURSED],
1178 ["Jewelery" => PICKUP_JEWELS], 1397 ["Jewelery" => PICKUP_JEWELS],
1398 ["Flesh" => PICKUP_FLESH],
1179 ], 1399 ],
1180 ["Weight/Value ratio", 2, 17] 1400 ["Weight/Value ratio", 2, 17]
1181 ) 1401 )
1182 { 1402 {
1183 my ($title, $x, $y, @bits) = @$_; 1403 my ($title, $x, $y, @bits) = @$_;
1184 $table->add ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]); 1404 $table->add_at ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
1185 1405
1186 for (@bits) { 1406 for (@bits) {
1187 ++$y; 1407 ++$y;
1188 1408
1189 my $mask = $_->[1]; 1409 my $mask = $_->[1];
1190 $table->add ($x , $y, new CFPlus::UI::Label text => $_->[0], align => 1, expand => 1); 1410 $table->add_at ($x , $y, new CFPlus::UI::Label text => $_->[0], align => 1, expand => 1);
1191 $table->add ($x+1, $y, my $checkbox = new CFPlus::UI::CheckBox 1411 $table->add_at ($x+1, $y, my $checkbox = new CFPlus::UI::CheckBox
1192 state => $::CFG->{pickup} & $mask, 1412 state => $::CFG->{pickup} & $mask,
1193 on_changed => sub { 1413 on_changed => sub {
1194 my ($box, $value) = @_; 1414 my ($box, $value) = @_;
1195 1415
1196 if ($value) { 1416 if ($value) {
1207 1427
1208 ${$_->[2]} = $checkbox if $_->[2]; 1428 ${$_->[2]} = $checkbox if $_->[2];
1209 } 1429 }
1210 } 1430 }
1211 1431
1212 $table->add (2, 18, new CFPlus::UI::ValSlider 1432 $table->add_at (2, 18, new CFPlus::UI::ValSlider
1213 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1], 1433 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1],
1214 template => ">= 99", 1434 template => ">= 99",
1215 to_value => sub { ">= " . 5 * $_[0] }, 1435 to_value => sub { ">= " . 5 * $_[0] },
1216 on_changed => sub { 1436 on_changed => sub {
1217 my ($slider, $value) = @_; 1437 my ($slider, $value) = @_;
1220 $::CFG->{pickup} |= int $value 1440 $::CFG->{pickup} |= int $value
1221 if $value; 1441 if $value;
1222 1; 1442 1;
1223 }); 1443 });
1224 1444
1225 $table->add (3, 18, new CFPlus::UI::Button 1445 $table->add_at (3, 18, new CFPlus::UI::Button
1226 text => "set", 1446 text => "set",
1227 on_activate => sub { 1447 on_activate => sub {
1228 $::CONN->send_command ("pickup $::CFG->{pickup}") 1448 $::CONN->send_command ("pickup $::CFG->{pickup}")
1229 if defined $::CONN; 1449 if defined $::CONN;
1230 0 1450 0
1231 }); 1451 });
1232 1452
1233 $table 1453 $r
1234} 1454}
1235 1455
1236my %SORT_ORDER = ( 1456my %SORT_ORDER = (
1237 type => undef, 1457 type => undef,
1238 mtime => sub { sort { 1458 mtime => sub {
1459 my $NOW = time;
1460 sort {
1461 my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6;
1462 my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6;
1463
1239 ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED) 1464 ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1240 or $b->{mtime} <=> $a->{mtime} 1465 or $btime <=> $atime
1241 or $a->{type} <=> $b->{type} 1466 or $a->{type} <=> $b->{type}
1467 } @_
1242 } @_ }, 1468 },
1243 weight => sub { sort { 1469 weight => sub { sort {
1244 $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1) 1470 $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1245 or $a->{type} <=> $b->{type} 1471 or $a->{type} <=> $b->{type}
1246 } @_ }, 1472 } @_ },
1247); 1473);
1272 #TODO# update to weigh/maxweight 1498 #TODO# update to weigh/maxweight
1273 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1); 1499 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1);
1274 1500
1275 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1); 1501 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1276 $sw1->add ($INV = new CFPlus::UI::Inventory); 1502 $sw1->add ($INV = new CFPlus::UI::Inventory);
1503 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}});
1277 1504
1278 $hb->add (my $vb2 = new CFPlus::UI::VBox); 1505 $hb->add (my $vb2 = new CFPlus::UI::VBox);
1279 1506
1280 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox); 1507 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox);
1281 1508
1310 has_close_button => 1 1537 has_close_button => 1
1311 ; 1538 ;
1312 1539
1313 my $ntb = 1540 my $ntb =
1314 $PL_NOTEBOOK = 1541 $PL_NOTEBOOK =
1315 new CFPlus::UI::Notebook expand => 1, debug => 1; 1542 new CFPlus::UI::Notebook expand => 1;
1316 1543
1317 $ntb->add ( 1544 $ntb->add_tab (
1318 "Statistics (F2)" => $STATS_PAGE = stats_window, 1545 "Statistics (F2)" => $STATS_PAGE = stats_window,
1319 "Shows statistics, where all your Stats and Resistances are shown." 1546 "Shows statistics, where all your Stats and Resistances are shown."
1320 ); 1547 );
1321 $ntb->add ( 1548 $ntb->add_tab (
1322 "Skills (F3)" => $SKILL_PAGE = skill_window, 1549 "Skills (F3)" => $SKILL_PAGE = skill_window,
1323 "Shows all your Skills." 1550 "Shows all your Skills."
1324 ); 1551 );
1325 1552
1326 my $spellsw = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1); 1553 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1327 $spellsw->add ($SPELL_PAGE = new CFPlus::UI::SpellList); 1554 $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList);
1328 $ntb->add ( 1555 $ntb->add_tab (
1329 "Spellbook (F4)" => $spellsw, 1556 "Spellbook (F4)" => $spellsw,
1330 "Displays all spells you have and lets you edit keyboard shortcuts for them." 1557 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1331 ); 1558 );
1332 $ntb->add ( 1559 $ntb->add_tab (
1333 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget, 1560 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1334 "Toggles the inventory window, where you can manage your loot (or treasures :). " 1561 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1335 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory." 1562 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1336 ); 1563 );
1564 $ntb->add_tab (Pickup => autopickup_setup,
1565 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1337 1566
1338 $ntb->set_current_page ($INVENTORY_PAGE); 1567 $ntb->set_current_page ($INVENTORY_PAGE);
1339 1568
1340 $plwin->add ($ntb); 1569 $plwin->add ($ntb);
1341 $plwin 1570 $plwin
1342} 1571}
1343 1572
1344sub update_bindings {
1345 $BIND_UPD_CB->() if $BIND_UPD_CB;
1346}
1347
1348sub keyboard_setup { 1573sub keyboard_setup {
1349 my $binding_list = new CFPlus::UI::VBox; 1574 CFPlus::Macro::keyboard_setup
1350
1351 my $refresh;
1352 $refresh = $BIND_UPD_CB = sub {
1353 $binding_list->clear ();
1354
1355 return unless $PROFILE;
1356
1357 for my $mod (keys %{$PROFILE->{bindings}}) {
1358 for my $sym (keys %{$PROFILE->{bindings}{$mod}}) {
1359 my $cmds = $PROFILE->{bindings}{$mod}{$sym};
1360 next unless ref $cmds eq 'ARRAY' and @$cmds > 0;
1361
1362 my $lbl = join "; ", @$cmds;
1363 my $nam = CFPlus::BindingEditor::keycombo_to_name ($mod, $sym);
1364 $binding_list->add (my $hb = new CFPlus::UI::HBox);
1365 $hb->add (new CFPlus::UI::Button
1366 text => "delete",
1367 tooltip => "Deletes the binding",
1368 on_activate => sub {
1369 $binding_list->remove ($hb);
1370 delete $PROFILE->{bindings}{$mod}{$sym};
1371 0
1372 });
1373
1374 $hb->add (new CFPlus::UI::Button
1375 text => "edit",
1376 tooltip => "Edits the binding",
1377 on_activate => sub {
1378 $::BIND_EDITOR->set_binding (
1379 $mod, $sym, $PROFILE->{bindings}{$mod}{$sym},
1380 sub {
1381 my ($nmod, $nsym, $ncmds) = @_;
1382 $::BIND_EDITOR->cfg_unbind ($mod, $sym);
1383 $::BIND_EDITOR->cfg_bind ($nmod, $nsym, $ncmds);
1384 $refresh->();
1385 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1386 $SETUP_DIALOG->show;
1387 },
1388 sub {
1389 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1390 $SETUP_DIALOG->show;
1391 });
1392 $::BIND_EDITOR->show;
1393 $SETUP_DIALOG->hide;
1394 0
1395 });
1396
1397 $hb->add (new CFPlus::UI::Label text => "(Key: $nam)");
1398 $hb->add (new CFPlus::UI::Label text => $lbl, expand => 1);
1399 }
1400 }
1401 };
1402
1403 my $vb = new CFPlus::UI::VBox;
1404 $vb->add (new CFPlus::UI::FancyFrame
1405 label => "Options",
1406 child => (my $hb = new CFPlus::UI::HBox),
1407 );
1408 $hb->add (new CFPlus::UI::Label text => "only shift-up stops fire");
1409 $hb->add (new CFPlus::UI::CheckBox
1410 expand => 1,
1411 state => $CFG->{shift_fire_stop},
1412 tooltip => "If this checkbox is enabled you will stop fire only if you stop pressing shift",
1413 on_changed => sub {
1414 my ($cbox, $value) = @_;
1415 $CFG->{shift_fire_stop} = $value;
1416 0
1417 });
1418
1419 $vb->add (new CFPlus::UI::FancyFrame
1420 label => "Bindings",
1421 child => $binding_list);
1422 $vb->add (my $hb = new CFPlus::UI::HBox);
1423
1424 $hb->add (new CFPlus::UI::Button
1425 text => "record new",
1426 expand => 1,
1427 tooltip => "This button opens the binding editor with an empty binding.",
1428 on_activate => sub {
1429 $::BIND_EDITOR->set_binding (undef, undef, [],
1430 sub {
1431 my ($mod, $sym, $cmds) = @_;
1432 $::BIND_EDITOR->cfg_bind ($mod, $sym, $cmds);
1433 $refresh->();
1434 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1435 $SETUP_DIALOG->show;
1436 },
1437 sub {
1438 $SETUP_NOTEBOOK->set_current_page ($SETUP_KEYBOARD);
1439 $SETUP_DIALOG->show;
1440 },
1441 );
1442 $SETUP_DIALOG->hide;
1443 $::BIND_EDITOR->show;
1444 0
1445 },
1446 );
1447
1448 $hb->add (new CFPlus::UI::Button
1449 text => "close",
1450 tooltip => "Closes the binding window",
1451 expand => 1,
1452 on_activate => sub {
1453 $SETUP_DIALOG->hide;
1454 0
1455 }
1456 );
1457
1458 $refresh->();
1459
1460 $vb
1461} 1575}
1462 1576
1463sub help_window { 1577sub help_window {
1464 my $win = new CFPlus::UI::Toplevel 1578 my $win = new CFPlus::UI::Toplevel
1465 x => 'center', 1579 x => 'center',
1622 1736
1623sub show_tip_of_the_day { 1737sub show_tip_of_the_day {
1624 # find all tips 1738 # find all tips
1625 my @tod = CFPlus::Pod::find tip_of_the_day => "*"; 1739 my @tod = CFPlus::Pod::find tip_of_the_day => "*";
1626 1740
1627 my $todindex = $CFPlus::DB_STATE->get ("tip_of_the_day"); 1741 CFPlus::DB::get state => "tip_of_the_day", sub {
1742 my ($todindex) = @_;
1628 $todindex = 0 if $todindex >= @tod; 1743 $todindex = 0 if $todindex >= @tod;
1629 $CFPlus::DB_STATE->put (tip_of_the_day => $todindex + 1); 1744 CFPlus::DB::put state => tip_of_the_day => $todindex + 1, sub { };
1630 1745
1631 # create dialog 1746 # create dialog
1632 my $dialog; 1747 my $dialog;
1633 1748
1634 my $close = sub { 1749 my $close = sub {
1635 $dialog->destroy; 1750 $dialog->destroy;
1751 };
1752
1753 $dialog = new CFPlus::UI::Toplevel
1754 x => "center",
1755 y => "center",
1756 z => 3,
1757 name => 'tip_of_the_day',
1758 force_w => int $WIDTH * 4/9,
1759 force_h => int $WIDTH * 2/9,
1760 title => "Tip of the day #" . (1 + $todindex),
1761 child => my $vbox = new CFPlus::UI::VBox,
1762 has_close_button => 1,
1763 on_delete => $close,
1764 ;
1765
1766 $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1767 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1768 $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $tod[$todindex]);
1769
1770 $vbox->add (my $table = new CFPlus::UI::Table col_expand => [0, 1]);
1771
1772 $table->add_at (0, 0, new CFPlus::UI::Button
1773 text => "Close",
1774 tooltip => "Close the tip of the day window. To never see it again, disable the tip of the day in the <b>Server Setup</b>.",
1775 on_activate => $close,
1776 );
1777
1778 $table->add_at (2, 0, new CFPlus::UI::Button
1779 text => "Next",
1780 tooltip => "Show the next <b>Tip of the day</b>.",
1781 on_activate => sub {
1782 $close->();
1783 &show_tip_of_the_day;
1784 },
1785 );
1786
1787 $dialog->show;
1636 }; 1788 };
1637
1638 $dialog = new CFPlus::UI::Toplevel
1639 x => "center",
1640 y => "center",
1641 z => 3,
1642 name => 'tip_of_the_day',
1643 force_w => int $WIDTH * 4/9,
1644 force_h => int $WIDTH * 2/9,
1645 title => "Tip of the day #" . (1 + $todindex),
1646 child => my $vbox = new CFPlus::UI::VBox,
1647 has_close_button => 1,
1648 on_delete => $close,
1649 ;
1650
1651 $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1652 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1653 $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $tod[$todindex]);
1654
1655 $vbox->add (my $table = new CFPlus::UI::Table);
1656
1657 $table->add (0, 0, new CFPlus::UI::Button
1658 text => "Close",
1659 tooltip => "Close the tip of the day window. To never see it again, disable the tip of the day in the <b>Server Setup</b>.",
1660 on_activate => $close,
1661 );
1662
1663 $table->add (2, 0, new CFPlus::UI::Button
1664 text => "Next",
1665 tooltip => "Show the next <b>Tip of the day</b>.",
1666 on_activate => sub {
1667 $close->();
1668 &show_tip_of_the_day;
1669 },
1670 );
1671
1672 $dialog->show;
1673} 1789}
1674 1790
1675sub sdl_init { 1791sub sdl_init {
1676 CFPlus::SDL_Init 1792 CFPlus::SDL_Init
1677 and die "SDL::Init failed!\n"; 1793 and die "SDL::Init failed!\n";
1678} 1794}
1679 1795
1680sub video_init { 1796sub video_init {
1681 sdl_init;
1682
1683 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES; 1797 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1684 1798
1685 my ($old_w, $old_h) = ($WIDTH, $HEIGHT); 1799 my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1686 1800
1687 ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] }; 1801 ($WIDTH, $HEIGHT, my ($rgb, $alpha)) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1688 $FULLSCREEN = $CFG->{fullscreen}; 1802 $FULLSCREEN = $CFG->{fullscreen};
1689 $FAST = $CFG->{fast}; 1803 $FAST = $CFG->{fast};
1690 1804
1691 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN 1805 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $rgb, $alpha, $FULLSCREEN
1692 or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n"; 1806 or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n";
1693 1807
1694 $SDL_ACTIVE = 1; 1808 $SDL_ACTIVE = 1;
1695 $LAST_REFRESH = time - 0.01; 1809 $LAST_REFRESH = time - 0.01;
1696 1810
1711 padding => 0, 1825 padding => 0,
1712 z => 100, 1826 z => 100,
1713 force_x => "max", 1827 force_x => "max",
1714 force_y => 0; 1828 force_y => 0;
1715 $DEBUG_STATUS->show; 1829 $DEBUG_STATUS->show;
1716
1717 $BIND_EDITOR = new CFPlus::BindingEditor (x => "max", y => 0);
1718 1830
1719 $STATUSBOX = new CFPlus::UI::Statusbox; 1831 $STATUSBOX = new CFPlus::UI::Statusbox;
1720 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]); 1832 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1721 1833
1722 (new CFPlus::UI::Frame 1834 (new CFPlus::UI::Frame
1740 1852
1741 $MAPWIDGET = new CFPlus::MapWidget; 1853 $MAPWIDGET = new CFPlus::MapWidget;
1742 $MAPWIDGET->connect (activate_console => sub { 1854 $MAPWIDGET->connect (activate_console => sub {
1743 my ($mapwidget, $preset) = @_; 1855 my ($mapwidget, $preset) = @_;
1744 1856
1745 if ($CONSOLE) { 1857 $MESSAGE_WINDOW->activate_console ($preset)
1746 $CONSOLE->{input}->{auto_activated} = 1; 1858 if $MESSAGE_WINDOW;
1747 $CONSOLE->{input}->grab_focus;
1748
1749 if ($preset && $CONSOLE->{input}->get_text eq '') {
1750 $CONSOLE->{input}->set_text ($preset);
1751 }
1752 }
1753 }); 1859 });
1754 $MAPWIDGET->show; 1860 $MAPWIDGET->show;
1755 $MAPWIDGET->grab_focus; 1861 $MAPWIDGET->grab_focus;
1756
1757 $LOGVIEW = new CFPlus::UI::TextScroller
1758 expand => 1,
1759 font => $FONT_FIXED,
1760 fontsize => $::CFG->{log_fontsize},
1761 indent => -4,
1762 can_hover => 1,
1763 can_events => 1,
1764 tooltip => "<b>Server Log</b>. This text viewer contains all the messages sent by the server.",
1765 ;
1766 1862
1767 $SETUP_DIALOG = new CFPlus::UI::Toplevel 1863 $SETUP_DIALOG = new CFPlus::UI::Toplevel
1768 title => "Setup", 1864 title => "Setup",
1769 name => "setup_dialog", 1865 name => "setup_dialog",
1770 x => 'center', 1866 x => 'center',
1774 force_h => $::HEIGHT * 0.6, 1870 force_h => $::HEIGHT * 0.6,
1775 has_close_button => 1, 1871 has_close_button => 1,
1776 ; 1872 ;
1777 1873
1778 $METASERVER = metaserver_dialog; 1874 $METASERVER = metaserver_dialog;
1875 $MESSAGE_WINDOW = new CFPlus::UI::MessageWindow;
1779 1876
1780 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1, 1877 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1,
1781 filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1); 1878 filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1782 1879
1783 $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup, 1880 $SETUP_NOTEBOOK->add_tab (Server => $SETUP_SERVER = server_setup,
1784 "Configure the server to play on, your username, password and other server-related options."); 1881 "Configure the server to play on, your username, password and other server-related options.");
1785 $SETUP_NOTEBOOK->add (Pickup => autopickup_setup, 1882 $SETUP_NOTEBOOK->add_tab (Client => client_setup,
1786 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them."); 1883 "Configure various client-specific settings.");
1787 $SETUP_NOTEBOOK->add (Graphics => graphics_setup, 1884 $SETUP_NOTEBOOK->add_tab (Graphics => graphics_setup,
1788 "Configure the video mode, performance, fonts and other graphical aspects of the game."); 1885 "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1789 $SETUP_NOTEBOOK->add (Audio => audio_setup, 1886 $SETUP_NOTEBOOK->add_tab (Audio => audio_setup,
1790 "Configure the use of audio, sound effects and background music."); 1887 "Configure the use of audio, sound effects and background music.");
1791 $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup, 1888 $SETUP_NOTEBOOK->add_tab (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1792 "Lets you define, edit and delete key bindings." 1889 "Lets you define, edit and delete key bindings."
1793 . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor " 1890 . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor "
1794 . "with nothing set and the recording started. After doing the actions you " 1891 . "with nothing set and the recording started. After doing the actions you "
1795 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. " 1892 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
1796 . "After pressing the combo the binding will be saved automatically and the " 1893 . "After pressing the combo the binding will be saved automatically and the "
1797 . "binding editor closes"); 1894 . "binding editor closes");
1798 $SETUP_NOTEBOOK->add (Debug => debug_setup, 1895 $SETUP_NOTEBOOK->add_tab (Debug => debug_setup,
1799 "Some debuggin' options. Do not ask."); 1896 "Some debuggin' options. Do not ask.");
1800 1897
1801 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top 1898 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1802 1899
1803 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG, 1900 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1804 tooltip => "Toggles a dialog where you can configure all aspects of this client."); 1901 tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1805 1902
1806 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW = message_window, 1903 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW,
1807 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server."); 1904 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1808 1905
1809 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 1906 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
1810 1907
1811 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window, 1908 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window,
1822 }, 1919 },
1823 ); 1920 );
1824 1921
1825 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window, 1922 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window,
1826 tooltip => "View Documentation"); 1923 tooltip => "View Documentation");
1924
1827 1925
1828 $BUTTONBAR->add (new CFPlus::UI::Button 1926 $BUTTONBAR->add (new CFPlus::UI::Button
1829 text => "Quit", 1927 text => "Quit",
1830 tooltip => "Terminates the program", 1928 tooltip => "Terminates the program",
1831 on_activate => sub { 1929 on_activate => sub {
1849 CFPlus::OpenGL::shutdown; 1947 CFPlus::OpenGL::shutdown;
1850 1948
1851 undef $SDL_ACTIVE; 1949 undef $SDL_ACTIVE;
1852} 1950}
1853 1951
1854my @bgmusic = qw(game1.ogg game2.ogg game3.ogg game5.ogg game6.ogg ross1.ogg ross2.ogg ross3.ogg ross4.ogg ross5.ogg); #d#
1855my $bgmusic;#TODO#hack#d#
1856
1857sub audio_channel_finished {
1858 my ($channel) = @_;
1859
1860 #warn "channel $channel finished\n";#d#
1861}
1862
1863sub audio_music_finished {
1864 return unless $CFG->{bgm_enable};
1865
1866 # TODO: hack, do play loop and mood music
1867 $bgmusic = new_from_file CFPlus::MixMusic CFPlus::find_rcfile "music/$bgmusic[0]";
1868 $bgmusic->play (0);
1869
1870 push @bgmusic, shift @bgmusic;
1871}
1872
1873sub audio_init { 1952sub audio_init {
1874 if ($CFG->{audio_enable}) { 1953 if ($CFG->{audio_enable}) {
1875 if (open my $fh, "<", CFPlus::find_rcfile "sounds/config") { 1954 $ENV{MIX_EFFECTSMAXSPEED} = 1;
1876 $SDL_MIXER = !CFPlus::Mix_OpenAudio; 1955 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
1877 1956
1878 unless ($SDL_MIXER) { 1957 unless ($SDL_MIXER) {
1879 status "Unable to open sound device: there will be no sound"; 1958 status "Unable to open sound device: there will be no sound";
1880 return; 1959 return;
1881 }
1882
1883 CFPlus::Mix_AllocateChannels 8;
1884 CFPlus::MixMusic::volume $CFG->{bgm_volume} * 128;
1885
1886 audio_music_finished;
1887
1888 while (<$fh>) {
1889 next if /^\s*#/;
1890 next if /^\s*$/;
1891
1892 my ($file, $volume, $event) = split /\s+/, $_, 3;
1893
1894 push @SOUNDS, "$volume,$file";
1895
1896 $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1897 my $chunk = new_from_file CFPlus::MixChunk CFPlus::find_rcfile "sounds/$file";
1898 $chunk->volume ($volume * 128 / 100);
1899 $chunk
1900 };
1901 }
1902 } else {
1903 status "unable to open sound config: $!";
1904 } 1960 }
1961
1962 CFPlus::Mix_AllocateChannels 16;
1963
1964 audio_music_finished;
1965 } else {
1966 undef $SDL_MIXER;
1905 } 1967 }
1906} 1968}
1907 1969
1908sub audio_shutdown { 1970sub audio_shutdown {
1909 CFPlus::Mix_CloseAudio if $SDL_MIXER; 1971 CFPlus::Mix_CloseAudio if $SDL_MIXER;
1910 undef $SDL_MIXER; 1972 undef $SDL_MIXER;
1911 @SOUNDS = (); 1973 @SOUNDS = ();
1912 %AUDIO_CHUNKS = (); 1974 %AUDIO_CHUNK = ();
1913} 1975}
1914 1976
1915my %animate_object; 1977my %animate_object;
1916my $animate_timer; 1978my $animate_timer;
1917 1979
1918my $fps = 9; 1980my $fps = 9;
1919
1920my %demo;#d#
1921 1981
1922sub force_refresh { 1982sub force_refresh {
1923 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05; 1983 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1924 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4; 1984 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1925 1985
1926 $CFPlus::UI::ROOT->draw; 1986 $CFPlus::UI::ROOT->draw;
1927 1987
1928 $WANT_REFRESH = 0; 1988 $WANT_REFRESH = 0;
1929 $CAN_REFRESH = 0; 1989 $CAN_REFRESH = 0;
1930 $LAST_REFRESH = $NOW; 1990 $LAST_REFRESH = $NOW;
1931 1991
1932 CFPlus::SDL_GL_SwapBuffers; 1992 CFPlus::SDL_GL_SwapBuffers;
1933} 1993}
1934 1994
1935my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub { 1995my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1936 $NOW = time; 1996 $NOW = time;
1937 1997
1938 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_) 1998 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1939 for CFPlus::SDL_PollEvent; 1999 for CFPlus::poll_events;
1940 2000
1941 if (%animate_object) { 2001 if (%animate_object) {
1942 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object; 2002 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1943 $WANT_REFRESH++; 2003 ++$WANT_REFRESH;
1944 } 2004 }
1945 2005
1946 if ($WANT_REFRESH) { 2006 if ($WANT_REFRESH) {
1947 force_refresh; 2007 force_refresh;
1948 } else { 2008 } else {
1958sub animation_stop { 2018sub animation_stop {
1959 my ($widget) = @_; 2019 my ($widget) = @_;
1960 delete $animate_object{$widget}; 2020 delete $animate_object{$widget};
1961} 2021}
1962 2022
1963# check once/second for faces that need to be prefetched
1964# this should, of course, only run on demand, but
1965# SDL forces worse things on us....
1966
1967Event->timer (after => 1, interval => 0.25, cb => sub {
1968 $CONN->face_prefetch
1969 if $CONN;
1970});
1971
1972%SDL_CB = ( 2023%SDL_CB = (
1973 CFPlus::SDL_QUIT => sub { 2024 CFPlus::SDL_QUIT => sub {
1974 Event::unloop -1; 2025 exit;
1975 }, 2026 },
1976 CFPlus::SDL_VIDEORESIZE => sub { 2027 CFPlus::SDL_VIDEORESIZE => sub {
1977 }, 2028 },
1978 CFPlus::SDL_VIDEOEXPOSE => sub { 2029 CFPlus::SDL_VIDEOEXPOSE => sub {
1979 CFPlus::UI::full_refresh; 2030 CFPlus::UI::full_refresh;
1980 }, 2031 },
1981 CFPlus::SDL_ACTIVEEVENT => sub { 2032 CFPlus::SDL_ACTIVEEVENT => sub {
1982# printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d# 2033# not useful, as APPACTIVE include sonly iconified state, not unmapped
2034# printf "active %x %x %x\n", $_[0]{gain}, $_[0]{state}, CFPlus::SDL_GetAppState;#d#
2035# printf "a %x\n", CFPlus::SDL_GetAppState & CFPlus::SDL_APPACTIVE;#d#
2036# printf "A\n" if $_[0]{state} & CFPlus::SDL_APPACTIVE;
2037# printf "K\n" if $_[0]{state} & CFPlus::SDL_APPINPUTFOCUS;
2038# printf "M\n" if $_[0]{state} & CFPlus::SDL_APPMOUSEFOCUS;
1983 }, 2039 },
1984 CFPlus::SDL_KEYDOWN => sub { 2040 CFPlus::SDL_KEYDOWN => sub {
1985 if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) { 2041 if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) {
1986 # alt-enter 2042 # alt-enter
1987 $FULLSCREEN_ENABLE->toggle; 2043 $FULLSCREEN_ENABLE->toggle;
2008 2064
2009$SIG{INT} = $SIG{TERM} = sub { exit }; 2065$SIG{INT} = $SIG{TERM} = sub { exit };
2010 2066
2011{ 2067{
2012 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc"; 2068 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc";
2069 CFPlus::DB::Server::run;
2070
2013 CFPlus::UI::set_layout ($::CFG->{layout}); 2071 CFPlus::UI::set_layout ($::CFG->{layout});
2014 2072
2015 my %DEF_CFG = ( 2073 my %DEF_CFG = (
2016 sdl_mode => 0, 2074 sdl_mode => 0,
2017 width => 640,
2018 height => 480,
2019 fullscreen => 0, 2075 fullscreen => 0,
2020 fast => 0, 2076 fast => 0,
2021 map_scale => 1, 2077 map_scale => 1,
2022 fow_enable => 1, 2078 fow_enable => 1,
2023 fow_intensity => 0.45, 2079 fow_intensity => 0,
2024 fow_smooth => 0, 2080 map_smoothing => 1,
2025 gui_fontsize => 1, 2081 gui_fontsize => 1,
2026 log_fontsize => 0.7, 2082 log_fontsize => 0.7,
2027 gauge_fontsize => 1, 2083 gauge_fontsize => 1,
2028 gauge_size => 0.35, 2084 gauge_size => 0.35,
2029 stat_fontsize => 0.7, 2085 stat_fontsize => 0.7,
2030 mapsize => 100, 2086 mapsize => 100,
2031 say_command => 'say',
2032 audio_enable => 1, 2087 audio_enable => 1,
2033 bgm_enable => 1, 2088 bgm_enable => 1,
2034 bgm_volume => 0.25, 2089 bgm_volume => 0.25,
2035 face_prefetch => 0,
2036 output_sync => 1, 2090 output_sync => 1,
2037 output_count => 1, 2091 output_count => 1,
2092 output_rate => "",
2038 pickup => 0, 2093 pickup => 0,
2039 inv_sort => "mtime", 2094 inv_sort => "mtime",
2040 default => "profile", # default profile 2095 default => "profile", # default profile
2041 show_tips => 1, 2096 show_tips => 1,
2097 logview_max_par => 1000,
2042 ); 2098 );
2043 2099
2044 while (my ($k, $v) = each %DEF_CFG) { 2100 while (my ($k, $v) = each %DEF_CFG) {
2045 $CFG->{$k} = $v unless exists $CFG->{$k}; 2101 $CFG->{$k} = $v unless exists $CFG->{$k};
2046 } 2102 }
2047 2103
2048 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de"; 2104 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
2105 $PROFILE = $CFG->{profile}{default};
2106
2107 # convert old bindings (only default profile matters)
2108 if (my $bindings = delete $PROFILE->{bindings}) {
2109 while (my ($mod, $syms) = each %$bindings) {
2110 while (my ($sym, $cmds) = each %$syms) {
2111 push @{ $PROFILE->{macro} }, {
2112 accelkey => [$mod*1, $sym*1],
2113 action => $cmds,
2114 };
2115 }
2116 }
2117 }
2049 2118
2050 sdl_init; 2119 sdl_init;
2051 2120
2052 @SDL_MODES = reverse 2121 @SDL_MODES = CFPlus::SDL_ListModes 8, 8;
2053 grep $_->[0] >= 640 && $_->[1] >= 480, 2122 @SDL_MODES = CFPlus::SDL_ListModes 5, 0 unless @SDL_MODES;
2054 CFPlus::SDL_ListModes;
2055
2056 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)"; 2123 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
2124
2125 @SDL_MODES = sort { $a->[0] * $a->[1] <=> $b->[0] * $b->[1] } @SDL_MODES;
2057 2126
2058 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES; 2127 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
2059 2128
2060 { 2129 {
2061 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw( 2130 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw(
2090# } 2159# }
2091# my $t2 = Time::HiRes::time; 2160# my $t2 = Time::HiRes::time;
2092# warn $t2-$t1; 2161# warn $t2-$t1;
2093# } 2162# }
2094 2163
2164 $startup_done->();
2165
2095 video_init; 2166 video_init;
2096 audio_init; 2167 audio_init;
2097} 2168}
2098 2169
2099show_tip_of_the_day if $CFG->{show_tips}; 2170show_tip_of_the_day if $CFG->{show_tips};
2100
2101use Data::Dumper; warn Dumper [CFPlus::win32_proxy_info()];#d#
2102 2171
2103Event::loop; 2172Event::loop;
2104#CFPlus::SDL_Quit; 2173#CFPlus::SDL_Quit;
2105#CFPlus::_exit 0; 2174#CFPlus::_exit 0;
2106 2175
2176END {
2107END { CFPlus::SDL_Quit } 2177 CFPlus::SDL_Quit;
2178 CFPlus::DB::Server::stop;
2179}
2108 2180
2109=head1 NAME 2181=head1 NAME
2110 2182
2111cfplus - A Crossfire+ and Crossfire game client 2183cfplus - A Crossfire+ and Crossfire game client
2112 2184

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines