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.198 by root, Tue Jul 31 02:45:39 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;
114our $STATWIDS; 159our $STATWIDS;
115 160
116our $SDL_ACTIVE; 161our $SDL_ACTIVE;
117our %SDL_CB; 162our %SDL_CB;
118 163
119our $SDL_MIXER;
120our @SOUNDS; # event => file mapping
121our %AUDIO_CHUNKS; # audio files
122
123our $ALT_ENTER_MESSAGE; 164our $ALT_ENTER_MESSAGE;
124our $STATUSBOX; 165our $STATUSBOX;
125our $DEBUG_STATUS; 166our $DEBUG_STATUS;
126 167
127our $INV; 168our $INV;
128our $INVR; 169our $INVR;
129our $INV_RIGHT_HB; 170our $INV_RIGHT_HB;
130 171
131our $BIND_EDITOR;
132our $BIND_UPD_CB;
133
134our $PICKUP_CFG; 172our $PICKUP_CFG;
173
174#############################################################################
135 175
136sub status { 176sub status {
137 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]); 177 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
138} 178}
139 179
140sub debug { 180sub debug {
141 $DEBUG_STATUS->set_text ($_[0]); 181 $DEBUG_STATUS->set_text ($_[0]);
142} 182}
183
184sub message {
185 my ($para) = @_;
186 $MESSAGE_WINDOW->message ($para);
187}
188
189#############################################################################
190#TODO: maybe move into own audio module...
191
192our $SDL_MIXER;
193
194our $MUSIC_DEFAULT = "in_a_heartbeat.ogg";
195our $MUSIC_WANT; # arryref of ambient music we want to play
196our @MUSIC_HAVE; # ambient music we have on disk
197our $MUSIC_START;
198our @MUSIC_JINGLE; # which jingles to play next
199our $MUSIC_PLAYING_DATA;
200our $MUSIC_PLAYING_META;
201our $MUSIC_PLAYER;
202our $MUSIC_RESUME = 30; # resume music when played less than these many seconds before
203
204our %AUDIO_CHUNK; # audio "files"
205our %AUDIO_PLAY; # which audio faces should be played
206
207sub audio_channel_finished {
208 my ($channel) = @_;
209
210# warn "channel $channel finished\n";#d#
211}
212
213sub audio_sound_push($) {
214 my ($face) = @_;
215
216 if (my $chunk = $AUDIO_CHUNK{$face}) {
217 for (grep $_->[0] >= Event::time, @{(delete $AUDIO_PLAY{$face}) || []}) {
218 my (undef, $dx, $dy, $vol) = @$_;
219
220 my $channel = CFPlus::Channel::find;
221 $channel->volume ($vol * 128 / 255);
222 $dx = $dx / 10 * 255;
223 $channel->set_panning (255 - $dx, 255 + $dx);
224
225# my $angle = $dx ? : $dx < 0 ?
226# my $distance = -$vol;
227# $channel->set_position ($angle, $distance);
228
229 $chunk->play ($channel);
230 }
231 } else {
232 # sound_meta not set means data is in flight either way
233 my $meta = $CONN->{sound_meta}{$face}
234 or return;
235
236 # if its a jingle, play it as ambient music
237 if ($meta->{meta}{jingle}) {
238 delete $AUDIO_PLAY{$face}; # take the jingle out of the sound queue
239 push @MUSIC_JINGLE, $meta; # push it oto the music/jingle queue
240 &audio_music_push ($face);
241 } else {
242 # fetch from database
243 CFPlus::DB::get res_data => $meta->{name}, sub {
244 my $rwops = new CFPlus::RW $_[0];
245 my $chunk = new CFPlus::MixChunk $rwops;
246 $chunk->volume (($meta->{meta}{volume} || 1) * 128);
247 $AUDIO_CHUNK{$face} = $chunk;
248
249 audio_sound_push ($face);
250 };
251 }
252 }
253}
254
255sub audio_sound_play {
256 my ($face, $dx, $dy, $vol) = @_;
257
258 $SDL_MIXER
259 or return;
260
261 my $queue = $AUDIO_PLAY{$face} ||= [];
262 push @$queue, [Event::time + 0.2, $dx, $dy, $vol]; # delay sound by max. 0.2s
263 audio_sound_push $face
264 unless @$queue > 1;
265}
266
267sub audio_music_update_volume {
268 return unless $MUSIC_PLAYING_META;
269 my $volume = $MUSIC_PLAYING_META->{meta}{volume} || 1;
270 my $base = $MUSIC_PLAYING_META->{meta}{jingle} ? 1 : $CFG->{bgm_volume};
271 CFPlus::MixMusic::volume $base * $volume * 128;
272}
273
274sub audio_music_start {
275 my $meta = $MUSIC_PLAYING_META;
276
277 CFPlus::DB::get res_data => $meta->{name}, sub {
278 return unless $SDL_MIXER;
279
280 # music might have changed...
281 $meta eq $MUSIC_PLAYING_META
282 or return &audio_music_start ();
283
284 audio_music_update_volume;
285
286 $MUSIC_PLAYING_DATA = \$_[0];
287
288 my $rwops = $meta->{path}
289 ? new_from_file CFPlus::RW $meta->{path}
290 : new CFPlus::RW $$MUSIC_PLAYING_DATA;
291
292 $MUSIC_PLAYER = new CFPlus::MixMusic $rwops
293 or ((warn CFPlus::Mix_GetError), return); # pretty fatal error
294
295 my $NOW = time;
296
297 if ($MUSIC_PLAYING_META->{stop_time} > $NOW - $MUSIC_RESUME) {
298 my $pos = $MUSIC_PLAYING_META->{stop_pos};
299 $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
300 $MUSIC_START = time - $pos;
301 } else {
302 $MUSIC_PLAYER->play (0);
303 $MUSIC_START = time;
304 }
305
306 delete $MUSIC_PLAYING_META->{stop_time};
307 delete $MUSIC_PLAYING_META->{stop_pos};
308 }
309}
310
311sub audio_music_push {
312 return unless $SDL_MIXER;
313
314 my $fade_out;
315
316 if (@MUSIC_JINGLE) {
317 @MUSIC_HAVE = $MUSIC_JINGLE[0];
318 $fade_out = 333;
319 } else {
320 return unless $CFG->{bgm_enable};
321
322 my @have =
323 grep $_,
324 map $CONN->{music_meta}{$_},
325 @$MUSIC_WANT;
326
327 @MUSIC_HAVE = @have
328 if @have;
329
330 # default MUSIC_HAVE == MUSIC_DEFAULT
331 @MUSIC_HAVE = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_HAVE;
332 $fade_out = 1000;
333 }
334
335 # if the currently playing song is acceptable, let it continue
336 return if $MUSIC_PLAYING_META
337 && grep $MUSIC_PLAYING_META == $_, @MUSIC_HAVE;
338
339 my $NOW = time;
340
341 if ($MUSIC_PLAYING_META) {
342 $MUSIC_PLAYING_META->{stop_time} = $NOW;
343 $MUSIC_PLAYING_META->{stop_pos} = $NOW - $MUSIC_START;
344 CFPlus::MixMusic::fade_out $fade_out;
345 } else {
346 # sort by stop time, oldest first
347 @MUSIC_HAVE = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_HAVE;
348
349 # if the most recently-played piece played very recently,
350 # resume it, else choose the oldest piece for rotation.
351 $MUSIC_PLAYING_META =
352 $MUSIC_HAVE[-1]{stop_time} > $NOW - $MUSIC_RESUME
353 ? $MUSIC_HAVE[-1]
354 : $MUSIC_HAVE[0];
355
356 audio_music_start;
357 }
358}
359
360sub audio_music_set_ambient {
361 my ($songs) = @_;
362
363 $MUSIC_WANT = $songs;
364 audio_music_push;
365}
366
367sub audio_music_finished {
368 # we compress multiple jingles of the same type
369 shift @MUSIC_JINGLE
370 while @MUSIC_JINGLE && $MUSIC_PLAYING_META == $MUSIC_JINGLE[0];
371
372 undef $MUSIC_PLAYER;
373 undef $MUSIC_PLAYING_META;
374 undef $MUSIC_PLAYING_DATA;
375
376 audio_music_push;
377}
378
379sub audio_init {
380 if ($CFG->{audio_enable}) {
381 $ENV{MIX_EFFECTSMAXSPEED} = 1;
382 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
383
384 unless ($SDL_MIXER) {
385 status "Unable to open sound device: there will be no sound";
386 return;
387 }
388
389 CFPlus::Mix_AllocateChannels 16;
390
391 audio_music_finished;
392 } else {
393 undef $SDL_MIXER;
394 }
395}
396
397sub audio_shutdown {
398 undef $MUSIC_PLAYER;
399 undef $MUSIC_PLAYING_META;
400 undef $MUSIC_PLAYING_DATA;
401
402 $MUSIC_WANT = [];
403 @MUSIC_JINGLE = ();
404 %AUDIO_PLAY = ();
405 %AUDIO_CHUNK = ();
406
407 CFPlus::Mix_CloseAudio if $SDL_MIXER;
408 undef $SDL_MIXER;
409}
410
411#############################################################################
143 412
144sub destroy_query_dialog { 413sub destroy_query_dialog {
145 (delete $_[0]{query_dialog})->destroy 414 (delete $_[0]{query_dialog})->destroy
146 if $_[0]{query_dialog}; 415 if $_[0]{query_dialog};
147} 416}
148 417
418# FIXME: a very ugly hack to wait for stat update look below! #d#
419our $QUERY_TIMER; #d#
420
149# server query dialog 421# server query dialog
150sub server_query { 422sub server_query {
151 my ($conn, $flags, $prompt) = @_; 423 my ($conn, $flags, $prompt) = @_;
424
425 # FIXME: a very ugly hack to wait for stat update #d#
426 if ($prompt =~ /roll new stats/ and not $conn->{stat_change_with}) {
427 unless ($QUERY_TIMER) {
428 $QUERY_TIMER =
429 Event->timer (
430 after => 1,
431 cb => sub {
432 server_query ($conn, $flags, $prompt, 1);
433 $QUERY_TIMER = undef
434 }
435 );
436 return;
437 }
438 }
152 439
153 $conn->{query_dialog} = my $dialog = new CFPlus::UI::Toplevel 440 $conn->{query_dialog} = my $dialog = new CFPlus::UI::Toplevel
154 x => "center", 441 x => "center",
155 y => "center", 442 y => "center",
156 title => "Server Query", 443 title => "Server Query",
157 child => my $vbox = new CFPlus::UI::VBox, 444 child => my $vbox = new CFPlus::UI::VBox,
158 ; 445 ;
159 446
160 my @dialog = my $label = new CFPlus::UI::Label 447 my @dialog = my $label = new CFPlus::UI::Label
161 max_w => $::WIDTH * 0.4, 448 max_w => $::WIDTH * 0.8,
162 ellipsise => 0, 449 ellipsise => 0,
163 text => $prompt; 450 text => $prompt;
164 451
165 if ($flags & CS_QUERY_YESNO) { 452 if ($flags & CS_QUERY_YESNO) {
166 push @dialog, my $hbox = new CFPlus::UI::HBox; 453 push @dialog, my $hbox = new CFPlus::UI::HBox;
183 ); 470 );
184 471
185 $dialog->grab_focus; 472 $dialog->grab_focus;
186 473
187 } elsif ($flags & CS_QUERY_SINGLECHAR) { 474 } 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) { 475 if ($prompt =~ /Now choose a character|Press any key for the next race/i) {
191 $MESSAGE_WINDOW->show; 476 $dialog->{tooltip} = "#charcreation_focus";
192 477
193 unshift @dialog, new CFPlus::UI::Label 478 unshift @dialog, new CFPlus::UI::Label
194 max_w => $::WIDTH * 0.4, 479 max_w => $::WIDTH * 0.8,
195 ellipsise => 0, 480 ellipsise => 0,
196 markup => "\nOr use your keyboard and the text entry below:\n"; 481 markup => "\nOr use your keyboard and the text entry below:\n";
197 482
198 unshift @dialog, my $table = new CFPlus::UI::Table; 483 unshift @dialog, my $table = new CFPlus::UI::Table;
199 484
200 $table->add (0, 0, new CFPlus::UI::Button 485 $table->add_at (0, 0, new CFPlus::UI::Button
201 text => "Next Race", 486 text => "Next Race",
202 on_activate => sub { 487 on_activate => sub {
203 $conn->send ("reply n"); 488 $conn->send ("reply n");
204 destroy_query_dialog $conn; 489 destroy_query_dialog $conn;
205 0 490 0
206 }, 491 },
207 ); 492 );
208 $table->add (2, 0, new CFPlus::UI::Button 493 $table->add_at (2, 0, new CFPlus::UI::Button
209 text => "Accept", 494 text => "Accept",
210 on_activate => sub { 495 on_activate => sub {
211 $conn->send ("reply d"); 496 $conn->send ("reply d");
212 destroy_query_dialog $conn; 497 destroy_query_dialog $conn;
213 0 498 0
214 }, 499 },
215 ); 500 );
216 501
502 if ($conn->{chargen_race_description}) {
503 unshift @dialog, new CFPlus::UI::Label
504 max_w => $::WIDTH * 0.8,
505 ellipsise => 0,
506 markup => "<span foreground='#ccccff'>$conn->{chargen_race_description}</span>",
507 ;
508 }
509
510 unshift @dialog, new CFPlus::UI::Face
511 face => $conn->{player}{face},
512 bg => [.2, .2, .2, 1],
513 min_w => 64,
514 min_h => 64,
515 ;
516
517 if ($conn->{chargen_race_title}) {
518 unshift @dialog, new CFPlus::UI::Label
519 allign => 1,
520 ellipsise => 0,
521 markup => "<span foreground='#ccccff' size='large'>Race: $conn->{chargen_race_title}</span>",
522 ;
523 }
524
217 unshift @dialog, new CFPlus::UI::Label 525 unshift @dialog, new CFPlus::UI::Label
218 max_w => $::WIDTH * 0.4, 526 max_w => $::WIDTH * 0.4,
219 ellipsise => 0, 527 ellipsise => 0,
220 markup => (CFPlus::Pod::section_label ui => "chargen_race"), 528 markup => (CFPlus::Pod::section_label ui => "chargen_race"),
221 ; 529 ;
225 $conn->send ("reply $stat"); 533 $conn->send ("reply $stat");
226 destroy_query_dialog $conn; 534 destroy_query_dialog $conn;
227 return; 535 return;
228 } 536 }
229 537
230 $STATS_PAGE->show;
231 $MESSAGE_WINDOW->hide;
232
233 unshift @dialog, new CFPlus::UI::Label 538 unshift @dialog, new CFPlus::UI::Label
234 max_w => $::WIDTH * 0.4, 539 max_w => $::WIDTH * 0.4,
235 ellipsise => 0, 540 ellipsise => 0,
236 markup => "\nOr use your keyboard and the text entry below:\n"; 541 markup => "\nOr use your keyboard and the text entry below:\n";
237 542
238 unshift @dialog, my $table = new CFPlus::UI::Table; 543 unshift @dialog, my $table = new CFPlus::UI::Table;
239 544
240 # left: re-roll 545 # left: re-roll
241 $table->add (0, 0, new CFPlus::UI::Button 546 $table->add_at (0, 0, new CFPlus::UI::Button
242 text => "Roll Again", 547 text => "Roll Again",
243 on_activate => sub { 548 on_activate => sub {
244 $conn->send ("reply y"); 549 $conn->send ("reply y");
245 destroy_query_dialog $conn; 550 destroy_query_dialog $conn;
246 0 551 0
260 [6 => "Pow", "Power ($conn->{stat}{+CS_STAT_POW})"], 565 [6 => "Pow", "Power ($conn->{stat}{+CS_STAT_POW})"],
261 [7 => "Cha", "Charisma ($conn->{stat}{+CS_STAT_CHA})"], 566 [7 => "Cha", "Charisma ($conn->{stat}{+CS_STAT_CHA})"],
262 ], 567 ],
263 ), 1 .. 2; 568 ), 1 .. 2;
264 569
265 $table->add (2, 0, new CFPlus::UI::Button 570 $table->add_at (2, 0, new CFPlus::UI::Button
266 text => "Swap Stats", 571 text => "Swap Stats",
267 on_activate => sub { 572 on_activate => sub {
268 $conn->{stat_change_with} = $sw2->{value}; 573 $conn->{stat_change_with} = $sw2->{value};
269 $conn->send ("reply $sw1->{value}"); 574 $conn->send ("reply $sw1->{value}");
270 destroy_query_dialog $conn; 575 destroy_query_dialog $conn;
271 0 576 0
272 }, 577 },
273 ); 578 );
274 $table->add (2, 1, new CFPlus::UI::HBox children => [$sw1, $sw2]); 579 $table->add_at (2, 1, new CFPlus::UI::HBox children => [$sw1, $sw2]);
275 580
276 # right: accept 581 # right: accept
277 $table->add (4, 0, new CFPlus::UI::Button 582 $table->add_at (4, 0, new CFPlus::UI::Button
278 text => "Accept", 583 text => "Accept",
279 on_activate => sub { 584 on_activate => sub {
280 $conn->send ("reply n"); 585 $conn->send ("reply n");
281 $STATS_PAGE->hide; 586 $STATS_PAGE->hide;
282 destroy_query_dialog $conn; 587 destroy_query_dialog $conn;
345 status "logging in..."; 650 status "logging in...";
346 651
347 $LOGIN_BUTTON->set_text ("Logout"); 652 $LOGIN_BUTTON->set_text ("Logout");
348 $SETUP_DIALOG->hide; 653 $SETUP_DIALOG->hide;
349 654
350 $PROFILE = $CFG->{profile}{default};
351
352 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32; 655 my $mapsize = List::Util::min 32, List::Util::max 11, int $WIDTH * $CFG->{mapsize} * 0.01 / 32;
353 656
354 my ($host, $port) = split /:/, $PROFILE->{host}; 657 my ($host, $port) = split /:/, $PROFILE->{host};
355 658
356 $MAP = new CFPlus::Map $mapsize, $mapsize; 659 $MAP = new CFPlus::Map;
357 660
358 $CONN = eval { 661 $CONN = eval {
359 new CFPlus::Protocol 662 new CFPlus::Protocol
360 host => $host, 663 host => $host,
361 port => $port || 13327, 664 port => $port || 13327,
362 user => $PROFILE->{user}, 665 user => $PROFILE->{user},
363 pass => $PROFILE->{password}, 666 pass => $PROFILE->{password},
364 mapw => $mapsize, 667 mapw => $mapsize,
365 maph => $mapsize, 668 maph => $mapsize,
366 669
367 client => "cfplus $VERSION $] $^O", 670 client => "cfplus $CFPlus::VERSION $] $^O",
368 671
369 map_widget => $MAPWIDGET, 672 map_widget => $MAPWIDGET,
370 logview => $LOGVIEW,
371 statusbox => $STATUSBOX, 673 statusbox => $STATUSBOX,
372 map => $MAP, 674 map => $MAP,
373 mapmap => $MAPMAP, 675 mapmap => $MAPMAP,
374 query => \&server_query, 676 query => \&server_query,
375 677
376 sound_play => sub { 678 setup_req => {
377 my ($x, $y, $soundnum, $type) = @_; 679 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 }, 680 },
387 }; 681 };
388 682
389 if ($CONN) { 683 if ($CONN) {
390 CFPlus::lowdelay fileno $CONN->{fh}; 684 CFPlus::lowdelay fileno $CONN->{fh};
399sub stop_game { 693sub stop_game {
400 $LOGIN_BUTTON->set_text ("Login"); 694 $LOGIN_BUTTON->set_text ("Login");
401 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER); 695 $SETUP_NOTEBOOK->set_current_page ($SETUP_SERVER);
402 $SETUP_DIALOG->show; 696 $SETUP_DIALOG->show;
403 $PL_WINDOW->hide; 697 $PL_WINDOW->hide;
404 $SPELL_PAGE->clear_spells; 698 $SPELL_LIST->clear_spells;
699 $CFPlus::UI::ROOT->emit (stop_game => ! ! $CONN);
700
701 &audio_music_set_ambient ([]);
405 702
406 return unless $CONN; 703 return unless $CONN;
407 704
408 status "connection closed"; 705 status "connection closed";
409 706
410 destroy_query_dialog $CONN; 707 destroy_query_dialog $CONN;
411 $CONN->destroy; 708 $CONN->destroy;
412 $CONN = 0; # false, does not autovivify 709 $CONN = 0; # false, does not autovivify
413 710
414 undef $MAP; 711 undef $MAP;
415 undef $PROFILE;
416} 712}
417 713
418sub graphics_setup { 714sub graphics_setup {
419 my $vbox = new CFPlus::UI::VBox; 715 my $vbox = new CFPlus::UI::VBox;
420 716
421 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]); 717 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]);
422 718
719 my $row = 0;
720
721 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "OpenGL Info");
722 $table->add_at (1, $row++, new CFPlus::UI::Label valign => 0, fontsize => 0.8, text => CFPlus::OpenGL::gl_vendor . ", " . CFPlus::OpenGL::gl_version,
723 can_events => 1,
724 tooltip => "<tt><span size='8192'>" . (CFPlus::OpenGL::gl_extensions) . "</span></tt>");
725
726 my $vidmode_tooltip =
727 "<b>Video Mode.</b> The video mode to use for fullscreen (and the window size for windowed operation). "
728 . "The format is <i>width</i> x <i>height</i> \@ <i>depth-per-channel</i> + <i>alpha-channel</i>.";
729
423 $table->add (0, 0, new CFPlus::UI::Label valign => 0, align => 1, text => "Video Mode"); 730 $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); 731 $table->add_at (1, $row++, my $hbox = new CFPlus::UI::HBox);
425 732
733 $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]); 734 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"); 735 tooltip => $vidmode_tooltip);
736 $hbox->add (my $mode_label = new CFPlus::UI::Label
737 align => 0, valign => 0, height => 0.8, template => "9999x9999@9+9",
738 can_events => 1, tooltip => $vidmode_tooltip);
428 739
429 $mode_slider->connect (changed => sub { 740 $mode_slider->connect (changed => sub {
430 my ($self, $value) = @_; 741 my ($self, $value) = @_;
431 742
432 $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value; 743 $CFG->{sdl_mode} = $self->{range}[0] = $value = int $value;
433 $mode_label->set_text (sprintf "%dx%d", @{$SDL_MODES[$value]}); 744 $mode_label->set_text (sprintf '%dx%d@%d+%d', @{$SDL_MODES[$value]});
434 }); 745 });
435 $mode_slider->emit (changed => $mode_slider->{range}[0]); 746 $mode_slider->emit (changed => $mode_slider->{range}[0]);
436 747
437 my $row = 1;
438
439 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fullscreen"); 748 $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 749 $table->add_at (1, $row++, $FULLSCREEN_ENABLE = new CFPlus::UI::CheckBox
441 state => $CFG->{fullscreen}, 750 state => $CFG->{fullscreen},
442 tooltip => "Bring the client into fullscreen mode.", 751 tooltip => "Bring the client into fullscreen mode.",
443 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 } 752 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 }
444 ); 753 );
445 754
446 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fast & Ugly"); 755 $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 756 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
448 state => $CFG->{fast}, 757 state => $CFG->{fast},
449 tooltip => "Lower the visual quality considerably to speed up rendering.", 758 tooltip => "Lower the visual quality considerably to speed up rendering.",
450 on_changed => sub { my ($self, $value) = @_; $CFG->{fast} = $value; 0 } 759 on_changed => sub { my ($self, $value) = @_; $CFG->{fast} = $value; 0 }
451 ); 760 );
452 761
762 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "GUI Fontsize");
763 $table->add_at (1, $row++, new CFPlus::UI::Slider
764 range => [$CFG->{gui_fontsize}, 0.5, 2, 0, 0.1],
765 tooltip => "The base font size used by most GUI elements that do not have their own setting.",
766 on_changed => sub { $CFG->{gui_fontsize} = $_[1]; 0 },
767 );
768
769 $table->add_at (1, $row++, new CFPlus::UI::Button
770 expand => 1, align => 0, text => "Apply",
771 tooltip => "Apply the video settings above.",
772 on_activate => sub {
773 video_shutdown ();
774 video_init ();
775 0
776 }
777 );
778
453 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Scale"); 779 $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 780 $table->add_at (1, $row++, new CFPlus::UI::Slider
455 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1], 781 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
456 tooltip => "Enlarge or shrink the displayed map. Changes are instant.", 782 tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
457 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 } 783 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 }
458 ); 784 );
459 785
786 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing");
787 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
788 state => $CFG->{map_smoothing},
789 tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. "
790 . "This increases load on the graphics subsystem and works only with 2.x servers. "
791 . "Changes take effect at next connection only.",
792 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 }
793 );
794
460 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War"); 795 $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 796 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
462 state => $CFG->{fow_enable}, 797 state => $CFG->{fow_enable},
463 tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.", 798 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 } 799 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 }
465 ); 800 );
466 801
467 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "FoW Intensity"); 802 $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 803 $table->add_at (1, $row++, new CFPlus::UI::Slider
469 range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256], 804 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.", 805 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 } 806 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_intensity} = $value; 0 }
472 ); 807 );
473 808
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"); 809 $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 810 $table->add_at (1, $row++, new CFPlus::UI::Slider
495 range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1], 811 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.", 812 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 }, 813 on_changed => sub { $MESSAGE_WINDOW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 },
498 ); 814 );
499 815
500 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge fontsize"); 816 $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 817 $table->add_at (1, $row++, new CFPlus::UI::Slider
502 range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1], 818 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.", 819 tooltip => "Adjusts the fontsize of the gauges at the bottom right. Changes are instant.",
504 on_changed => sub { 820 on_changed => sub {
505 $CFG->{gauge_fontsize} = $_[1]; 821 $CFG->{gauge_fontsize} = $_[1];
506 &set_gauge_window_fontsize; 822 &set_gauge_window_fontsize;
507 0 823 0
508 } 824 }
509 ); 825 );
510 826
511 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge size"); 827 $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 828 $table->add_at (1, $row++, new CFPlus::UI::Slider
513 range => [$CFG->{gauge_size}, 0.2, 0.8], 829 range => [$CFG->{gauge_size}, 0.2, 0.8],
514 tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.", 830 tooltip => "Adjust the size of the stats gauges at the bottom right. Changes are instant.",
515 on_changed => sub { 831 on_changed => sub {
516 $CFG->{gauge_size} = $_[1]; 832 $CFG->{gauge_size} = $_[1];
517 $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size}); 833 $GAUGES->{win}->set_size ($WIDTH, int $HEIGHT * $CFG->{gauge_size});
518 0 834 0
519 } 835 }
520 ); 836 );
521 837
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 838 $vbox
533} 839}
534 840
535sub audio_setup { 841sub audio_setup {
536 my $vbox = new CFPlus::UI::VBox; 842 my $vbox = new CFPlus::UI::VBox;
537 843
538 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]); 844 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]);
539 845
540 my $row = 0; 846 my $row = 0;
541 847
542 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable"); 848 $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 849 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
544 state => $CFG->{audio_enable}, 850 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.", 851 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 } 852 on_changed => sub { $CFG->{audio_enable} = $_[1]; 0 }
547 ); 853 );
548# $table->add (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume"); 854# $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 { 855# $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
550# $CFG->{effects_volume} = $_[1]; 856# $CFG->{effects_volume} = $_[1];
551# }); 857# });
552 $table->add (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music"); 858 $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); 859 $table->add_at (1, $row++, my $hbox = new CFPlus::UI::HBox);
554 $hbox->add (new CFPlus::UI::CheckBox 860 $hbox->add (new CFPlus::UI::CheckBox
555 expand => 1, state => $CFG->{bgm_enable}, 861 expand => 1, state => $CFG->{bgm_enable},
556 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.", 862 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 } 863 on_changed => sub { $CFG->{bgm_enable} = $_[1]; 0 }
558 ); 864 );
559 $hbox->add (new CFPlus::UI::Slider 865 $hbox->add (new CFPlus::UI::Slider
560 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128], 866 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
561 tooltip => "The volume of the background music. Changes are instant.", 867 tooltip => "The volume of the background music. Changes are instant.",
562 on_changed => sub { $CFG->{bgm_volume} = $_[1]; CFPlus::MixMusic::volume $_[1] * 128; 0 } 868 on_changed => sub { $CFG->{bgm_volume} = $_[1]; audio_music_update_volume; 0 }
563 ); 869 );
564 870
565 $table->add (1, $row++, new CFPlus::UI::Button 871 $table->add_at (1, $row++, new CFPlus::UI::Button
566 expand => 1, align => 0, text => "Apply", 872 expand => 1, align => 0, text => "Apply",
567 tooltip => "Apply the audio settings", 873 tooltip => "Apply the audio settings",
568 on_activate => sub { 874 on_activate => sub {
569 audio_shutdown (); 875 audio_shutdown ();
570 audio_init (); 876 audio_init ();
629} 935}
630 936
631sub debug_setup { 937sub debug_setup {
632 my $table = new CFPlus::UI::Table; 938 my $table = new CFPlus::UI::Table;
633 939
634 $table->add (0, 0, new CFPlus::UI::Label text => "Widget Borders"); 940 $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 }); 941 $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"); 942 $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 }); 943 $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"); 944 $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 }); 945 $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"); 946 $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 }); 947 $table->add_at (1, 3, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 });
948 $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } );
642 949
643 my @default_smooth = (0.05, 0.13, 0.05, 0.13, 0.30, 0.13, 0.05, 0.13, 0.05); 950 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d#
644 951
645 for my $x (0..2) { 952 $table->add_at (7,7, my $t = new CFPlus::UI::Table expand => 0);
646 for my $y (0 .. 2) { 953 $t->add_at (0,0, new CFPlus::UI::Label text => "a a a a", rowspan => 1, colspan => 2);
647 $table->add ($x + 3, $y, 954 $t->add_at (2,0, new CFPlus::UI::Label text => "b\nb", rowspan => 2, colspan => 1);
648 new CFPlus::UI::Entry 955 $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], 956 $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 }, 957 $t->add_at (1,1, new CFPlus::UI::Label text => "e");
651 ); 958
652 } 959 $table->add_at (7, 6, my $c = new CFPlus::UI::Canvas);
960
961 $c->add_items ({
962 type => "line_loop",
963 color => [0, 1, 0],
964 width => 9,
965 coord_mode => "abs",
966 coord => [[10, 5], [5, 50], [20, 5], [5, 60]],
653 } 967 });
654 968
969 $c->add_items ({
970 type => "lines",
971 color => [1, 1, 0],
972 width => 2,
973 coord_mode => "rel",
974 coord => [[0,0], [1,1], [1,0], [0,1]],
975 });
976
977 $c->add_items ({
978 type => "polygon",
979 color => [0, 0.43, 0],
980 width => 2,
981 coord_mode => "rel",
982 coord => [[0,0.2], [1,.4], [1,.6], [0,.8]],
983 });
655 984
656 $table 985 $table
657} 986}
658 987
659sub stats_window { 988sub stats_window {
707 [2, 4, st_spd => "Spd", 10.54], 1036 [2, 4, st_spd => "Spd", 10.54],
708 [2, 5, st_wspd => "WSp", 10.54], 1037 [2, 5, st_wspd => "WSp", 10.54],
709 ) { 1038 ) {
710 my ($col, $row, $id, $label, $template) = @$_; 1039 my ($col, $row, $id, $label, $template) = @$_;
711 1040
712 $tbl->add ($col , $row, $STATWIDS->{$id} = new CFPlus::UI::Label 1041 $tbl->add_at ($col , $row, $STATWIDS->{$id} = new CFPlus::UI::Label
713 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0, 1042 font => $FONT_FIXED, can_hover => 1, can_events => 1, valign => 0,
714 align => +1, template => $template, tooltip => "#stat_$label"); 1043 align => +1, template => $template, tooltip => "#stat_$label");
715 $tbl->add ($col + 1, $row, $STATWIDS->{"$id\_lbl"} = new CFPlus::UI::Label 1044 $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, 1045 font => $FONT_FIXED, can_hover => 1, can_events => 1, fg => $color2, valign => 0,
717 align => -1, text => $label, tooltip => "#stat_$label"); 1046 align => -1, text => $label, tooltip => "#stat_$label");
718 } 1047 }
719 1048
720 $vb->add (new CFPlus::UI::FancyFrame 1049 $vb->add (new CFPlus::UI::FancyFrame
761 cold => ["Cold", 1090 cold => ["Cold",
762 "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)"], 1091 "<b>Cold</b> (this is your resistance against cold spells like icestorm, snowstorm, ...)"],
763 ghit => ["Ghost hit", 1092 ghit => ["Ghost hit",
764 "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)"], 1093 "<b>Ghost hit</b> (special attack used by ghosts and ghost-like beings)"],
765 ); 1094 );
1095
766 for (qw/slow holyw conf fire depl magic 1096 for (qw/slow holyw conf fire depl magic
767 drain acid pois para deat phys 1097 drain acid pois para deat phys
768 blind fear tund elec cold ghit/) 1098 blind fear tund elec cold ghit/)
769 { 1099 {
770 $tbl2->add ($col, $row, 1100 $tbl2->add_at ($col, $row,
771 $STATWIDS->{"res_$_"} = 1101 $STATWIDS->{"res_$_"} =
772 new CFPlus::UI::Label 1102 new CFPlus::UI::Label
773 font => $FONT_FIXED, 1103 font => $FONT_FIXED,
774 template => "-100%", 1104 template => "-100%",
775 align => +1, 1105 align => +1,
776 valign => 0, 1106 valign => 0,
777 can_events => 1, 1107 can_events => 1,
778 can_hover => 1, 1108 can_hover => 1,
779 tooltip => $resist_names{$_}->[1], 1109 tooltip => $resist_names{$_}->[1],
780 ); 1110 );
781 $tbl2->add ($col + 1, $row, new CFPlus::UI::Image 1111 $tbl2->add_at ($col + 1, $row, new CFPlus::UI::Image
782 font => $FONT_FIXED, 1112 font => $FONT_FIXED,
783 can_hover => 1, 1113 can_hover => 1,
784 can_events => 1, 1114 can_events => 1,
785 path => "ui/resist/resist_$_.png", 1115 path => "ui/resist/resist_$_.png",
786 tooltip => $resist_names{$_}->[1], 1116 tooltip => $resist_names{$_}->[1],
787 ); 1117 );
788 $tbl2->add ($col + 2, $row, new CFPlus::UI::Label 1118 $tbl2->add_at ($col + 2, $row, new CFPlus::UI::Label
789 text => $resist_names{$_}->[0], 1119 text => $resist_names{$_}->[0],
790 font => $FONT_FIXED, 1120 font => $FONT_FIXED,
791 can_hover => 1, 1121 can_hover => 1,
792 can_events => 1, 1122 can_events => 1,
793 tooltip => $resist_names{$_}->[1], 1123 tooltip => $resist_names{$_}->[1],
826 return if $METASERVER_ATIME > time; 1156 return if $METASERVER_ATIME > time;
827 $METASERVER_ATIME = time + 60; 1157 $METASERVER_ATIME = time + 60;
828 1158
829 my $table = $METASERVER->{table}; 1159 my $table = $METASERVER->{table};
830 $table->clear; 1160 $table->clear;
831 $table->add (0, 0, my $label = new CFPlus::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list..."); 1161 $table->add_at (0, 0, my $label = new CFPlus::UI::Label max_w => $WIDTH * 0.8, text => "fetching server list...");
832 1162
833 my $buf; 1163 my $ok = 0;
834 1164
835 my $fh = new IO::Socket::INET PeerHost => $META_SERVER, Blocking => 0; 1165 CFPlus::background {
1166 my $ua = CFPlus::lwp_useragent;
836 1167
837 unless ($fh) { 1168 CFPlus::background_msg CFPlus::from_json +(CFPlus::lwp_check $ua->get ($META_SERVER))->decoded_content;
838 $label->set_text ("unable to contact metaserver: $!"); 1169 } sub {
839 return; 1170 my ($msg) = @_;
840 } 1171 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; 1172 $table->clear;
855 1173
856 my @tip = ( 1174 my @tip = (
857 "The current number of users logged in on the server.", 1175 "The current number of users logged in on the server.",
858 "The hostname of the server.", 1176 "The hostname of the server.",
859 "The time this server has been running without being restarted.", 1177 "The time this server has been running without being restarted.",
860 "The server software version - a '+' indicates a Crossfire+ server.", 1178 "The server software version - a '+' indicates a Crossfire+ server.",
861 "Short information about this server provided by its admins.", 1179 "Short information about this server provided by its admins.",
862 ); 1180 );
863 my @col = qw(#Users Host Uptime Version Description); 1181 my @col = qw(#Users Host Uptime Version Description);
864 $table->add ($_, 0, new CFPlus::UI::Label 1182 $table->add_at ($_, 0, new CFPlus::UI::Label
865 can_hover => 1, can_events => 1, 1183 can_hover => 1, can_events => 1,
866 align => 0, fg => [1, 1, 0], 1184 align => 0, fg => [1, 1, 0],
867 text => $col[$_], tooltip => $tip[$_]) 1185 text => $col[$_], tooltip => $tip[$_])
868 for 0 .. $#col; 1186 for 0 .. $#col;
869 1187
870 my @align = qw(1 0 1 1 -1); 1188 my @align = qw(1 0 1 1 -1);
871 1189
872 my $y = 0; 1190 my $y = 0;
873 for my $m (sort { $b->[3] <=> $a->[3] } map [split /\|/], split /\015?\012/, $buf) { 1191 for my $m (@{ $msg->{servers} }) {
874 my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime) = @$m; 1192 my ($ip, $last, $host, $users, $version, $desc, $ibytes, $obytes, $uptime, $highlight) =
1193 @$m{qw(ip age hostname users version description ibytes obytes uptime highlight)};
875 1194
876 for ($desc) { 1195 for ($desc) {
877 s/<br>/\n/gi; 1196 s/<br>/\n/gi;
878 s/<li>/\n· /gi; 1197 s/<li>/\n· /gi;
879 s/<.*?>//sgi; 1198 s/<.*?>//sgi;
880 s/&/&amp;/g; 1199 s/&amp;/&/g;
881 s/</&lt;/g; 1200 s/&lt;/</g;
882 s/>/&gt;/g; 1201 s/&gt;/>/g;
883 } 1202 }
884 1203
885 $uptime = sprintf "%dd %02d:%02d:%02d", 1204 $uptime = sprintf "%dd %02d:%02d:%02d",
886 (int $m->[8] / 86400), 1205 (int $uptime / 86400),
887 (int $m->[8] / 3600) % 24, 1206 (int $uptime / 3600) % 24,
888 (int $m->[8] / 60) % 60, 1207 (int $uptime / 60) % 60,
889 $m->[8] % 60; 1208 $uptime % 60;
890 1209
891 $m = [$users, $host, $uptime, $version, $desc]; 1210 $m = [$users, $host, $uptime, $version, $desc];
892 1211
893 $y++; 1212 $y++;
894 1213
895 $table->add (scalar @$m, $y, new CFPlus::UI::VBox children => [ 1214 $table->add_at (scalar @$m, $y, new CFPlus::UI::VBox children => [
896 (new CFPlus::UI::Button 1215 (new CFPlus::UI::Button
897 text => "Use", 1216 text => "Use",
898 tooltip => "Put this server into the <b>Host:Port</b> field", 1217 tooltip => "Put this server into the <b>Host:Port</b> field",
899 on_activate => sub { 1218 on_activate => sub {
900 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host); 1219 $HOST_ENTRY->set_text ($CFG->{profile}{default}{host} = $host);
903 }, 1222 },
904 ), 1223 ),
905 (new CFPlus::UI::Empty expand => 1), 1224 (new CFPlus::UI::Empty expand => 1),
906 ]); 1225 ]);
907 1226
908 $table->add ($_, $y, new CFPlus::UI::Label 1227 $table->add_at ($_, $y, new CFPlus::UI::Label
1228 max_w => $::WIDTH * 0.4,
909 ellipsise => 0, 1229 ellipsise => 0,
910 align => $align[$_], 1230 align => $align[$_],
911 text => $m->[$_], 1231 text => $m->[$_],
912 tooltip => $tip[$_], 1232 tooltip => $tip[$_],
1233 fg => ($highlight ? [1, 1, 1] : [.7, .7, .7]),
913 can_hover => 1, 1234 can_hover => 1,
914 can_events => 1, 1235 can_events => 1,
915 fontsize => 0.8) 1236 fontsize => 0.8)
916 for 0 .. $#$m; 1237 for 0 .. $#$m;
917 } 1238 }
1239 } else {
1240 $ok or $label->set_text ("error while contacting metaserver");
918 } 1241 }
919 }); 1242 };
1243
920} 1244}
921 1245
922sub metaserver_dialog { 1246sub metaserver_dialog {
923 my $vbox = new CFPlus::UI::VBox; 1247 my $vbox = new CFPlus::UI::VBox;
924 my $table = new CFPlus::UI::Table; 1248 my $table = new CFPlus::UI::Table;
928 title => "Server List", 1252 title => "Server List",
929 name => 'metaserver_dialog', 1253 name => 'metaserver_dialog',
930 x => 'center', 1254 x => 'center',
931 y => 'center', 1255 y => 'center',
932 z => 3, 1256 z => 3,
1257 force_w => $::WIDTH * 0.9,
933 force_h => $::HEIGHT * 0.4, 1258 force_h => $::HEIGHT * 0.7,
934 child => $vbox, 1259 child => $vbox,
935 has_close_button => 1, 1260 has_close_button => 1,
936 table => $table, 1261 table => $table,
937 on_visibility_change => sub { 1262 on_visibility_change => sub {
938 update_metaserver ($_[0]) if $_[1]; 1263 update_metaserver ($_[0]) if $_[1];
948 1273
949 $vbox->add (new CFPlus::UI::FancyFrame 1274 $vbox->add (new CFPlus::UI::FancyFrame
950 label => "Connection Settings", 1275 label => "Connection Settings",
951 child => (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]), 1276 child => (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]),
952 ); 1277 );
953 $table->add (0, 2, new CFPlus::UI::Label valign => 0, align => 1, text => "Host:Port"); 1278 $table->add_at (0, 2, new CFPlus::UI::Label valign => 0, align => 1, text => "Host:Port");
954 1279
955 { 1280 {
956 $table->add (1, 2, my $vbox = new CFPlus::UI::VBox); 1281 $table->add_at (1, 2, my $vbox = new CFPlus::UI::VBox);
957 1282
958 $vbox->add ( 1283 $vbox->add (
959 $HOST_ENTRY = new CFPlus::UI::Entry 1284 $HOST_ENTRY = new CFPlus::UI::Entry
960 expand => 1, 1285 expand => 1,
961 text => $CFG->{profile}{default}{host}, 1286 text => $CFG->{profile}{default}{host},
962 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to", 1287 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
963 on_changed => sub { 1288 on_changed => sub {
964 my ($self, $value) = @_; 1289 my ($self, $value) = @_;
965 $CFG->{profile}{default}{host} = $value; 1290 $CFG->{profile}{default}{host} = $value;
966 0 1291 1
967 } 1292 }
968 ); 1293 );
969 1294
970 $vbox->add (new CFPlus::UI::Button 1295 $vbox->add (new CFPlus::UI::Button
971 expand => 1, 1296 expand => 1,
972 text => "Server List", 1297 text => "Server List",
973 other => $METASERVER, 1298 other => $METASERVER,
974 tooltip => "Show a list of available crossfire servers", 1299 tooltip => "Show a list of available crossfire servers",
975 on_activate => sub { $METASERVER->toggle_visibility; 0 }, 1300 on_activate => sub { $METASERVER->toggle_visibility; 0 },
976 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 0 }, 1301 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 1 },
977 ); 1302 );
978 } 1303 }
979 1304
980 $table->add (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username"); 1305 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username");
981 $table->add (1, 4, new CFPlus::UI::Entry 1306 $table->add_at (1, 4, new CFPlus::UI::Entry
982 text => $CFG->{profile}{default}{user}, 1307 text => $CFG->{profile}{default}{user},
983 tooltip => "The name of your character on the server", 1308 tooltip => "The name of your character on the server",
984 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value } 1309 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value; 1 }
985 ); 1310 );
986 1311
987 $table->add (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password"); 1312 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password");
988 $table->add (1, 5, new CFPlus::UI::Entry 1313 $table->add_at (1, 5, new CFPlus::UI::Entry
989 text => $CFG->{profile}{default}{password}, 1314 text => $CFG->{profile}{default}{password},
990 hidden => 1, 1315 hidden => 1,
991 tooltip => "The password for your character", 1316 tooltip => "The password for your character",
992 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value } 1317 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value; 1 }
993 ); 1318 );
994 1319
995 $table->add (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size"); 1320 $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 1321 $table->add_at (1, 7, new CFPlus::UI::Slider
997 force_w => 100, 1322 force_w => 100,
998 range => [$CFG->{mapsize}, 10, 100, 0, 1], 1323 range => [$CFG->{mapsize}, 10, 100, 0, 1],
999 tooltip => "This is the size of the portion of the map update the server sends you. " 1324 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, " 1325 . "If you set this to a high value you will be able to see further, "
1001 . "but you also increase bandwidth requirements and latency. " 1326 . "but you also increase bandwidth requirements and latency. "
1002 . "This option is only used once at log-in.", 1327 . "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 }, 1328 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 1 },
1004 ); 1329 );
1005 1330
1006 $table->add (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Face Prefetch"); 1331 $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 1332 $table->add_at (1, 8, new CFPlus::UI::Entry
1008 state => $CFG->{face_prefetch}, 1333 text => $CFG->{output_rate},
1009 tooltip => "<b>Background Image Prefetch</b>\n\n" 1334 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. " 1335 . "when sending data. When 0 or unset, the server "
1011 . "This might increase or create lag, but increases the chances " 1336 . "default will be used, which is usually around 100kb/s. Most servers will "
1012 . "of faces being ready for display when you encounter them. " 1337 . "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 }, 1338 on_changed => sub { $CFG->{output_rate} = $_[1]; 1 },
1017 ); 1339 );
1018 1340
1019 $table->add (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count"); 1341 $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 1342 $table->add_at (1, 9, new CFPlus::UI::Entry
1021 text => $CFG->{output_count}, 1343 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.", 1344 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 }, 1345 on_changed => sub { $CFG->{output_count} = $_[1]; 1 },
1024 ); 1346 );
1025 1347
1026 $table->add (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync"); 1348 $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 1349 $table->add_at (1, 10, new CFPlus::UI::Entry
1028 text => $CFG->{output_sync}, 1350 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.", 1351 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 }, 1352 on_changed => sub { $CFG->{output_sync} = $_[1]; 1 },
1031 ); 1353 );
1032 1354
1033 $table->add (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button 1355 $table->add_at (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button
1034 expand => 1, 1356 expand => 1,
1035 align => 0, 1357 align => 0,
1036 text => "Login", 1358 text => "Login",
1037 on_activate => sub { 1359 on_activate => sub {
1038 $CONN ? stop_game 1360 $CONN ? stop_game
1039 : start_game; 1361 : start_game;
1040 0 1362 1
1041 }, 1363 },
1042 ); 1364 );
1043 1365
1044 $table->add (0, 12, new CFPlus::UI::Label valign => 0, align => 1, text => "Chat Command"); 1366 $vbox->add (new CFPlus::UI::FancyFrame
1045 $table->add (1, 12, my $saycmd = new CFPlus::UI::Entry 1367 label => "Server Info",
1046 text => $CFG->{say_command}, 1368 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 ); 1369 );
1056 1370
1371 $vbox
1372}
1373
1374sub client_setup {
1375 my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1];
1376
1377 my $row = 0;
1378
1057 $table->add (0, 13, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day"); 1379 $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 1380 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
1059 state => $CFG->{show_tips}, 1381 state => $CFG->{show_tips},
1060 tooltip => "Show the <b>Tip of the day</b> window at startup?", 1382 tooltip => "Show the <b>Tip of the day</b> window at startup?",
1061 on_changed => sub { 1383 on_changed => sub {
1062 my ($self, $value) = @_; 1384 my ($self, $value) = @_;
1063 $CFG->{shop_tips} = $value; 1385 $CFG->{show_tips} = $value;
1064 0 1386 0
1065 } 1387 }
1066 ); 1388 );
1067 1389
1068 $vbox->add (new CFPlus::UI::FancyFrame 1390 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Messages Window Size");
1069 label => "Server Info", 1391 $table->add_at (1, $row++, my $saycmd = new CFPlus::UI::Entry
1070 child => ($SERVER_INFO = new CFPlus::UI::Label ellipsise => 0), 1392 text => $CFG->{logview_max_par},
1071 ); 1393 tooltip => "This is maximum number of messages remembered in the <b>Messages</b> window. If the server "
1072 1394 . "sends more messages than this number, older messages get removed to save memory and "
1073 $vbox 1395 . "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 { 1396 on_changed => sub {
1096 my ($input, $prev_focus) = @_; 1397 my ($self, $value) = @_;
1097 1398 $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 1399 0
1106 }, 1400 },
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 }; 1401 );
1135 1402
1136 $window 1403 $table
1137} 1404}
1138 1405
1139sub autopickup_setup { 1406sub autopickup_setup {
1407 my $r = new CFPlus::UI::ScrolledWindow (
1408 expand => 1,
1409 scroll_y => 1
1410 );
1140 my $table = new CFPlus::UI::Table; 1411 $r->add (my $table = new CFPlus::UI::Table
1412 row_expand => [0],
1413 col_expand => [0, 1, 0, 1],
1414 );
1141 1415
1142 for ( 1416 for (
1143 ["General", 0, 0, 1417 ["General", 0, 0,
1144 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE], 1418 ["Enable autopickup" => PICKUP_NEWMODE, \$PICKUP_ENABLE],
1145 ["Inhibit autopickup" => PICKUP_INHIBIT], 1419 ["Inhibit autopickup" => PICKUP_INHIBIT],
1159 ["Boots" => PICKUP_BOOTS], 1433 ["Boots" => PICKUP_BOOTS],
1160 ["Gloves" => PICKUP_GLOVES], 1434 ["Gloves" => PICKUP_GLOVES],
1161 ["Cloaks" => PICKUP_CLOAK], 1435 ["Cloaks" => PICKUP_CLOAK],
1162 ], 1436 ],
1163 1437
1164 ["Readables", 2, 2, 1438 ["Readables", 2, 0,
1165 ["Spellbooks" => PICKUP_SPELLBOOK], 1439 ["Spellbooks" => PICKUP_SPELLBOOK],
1166 ["Skillscrolls" => PICKUP_SKILLSCROLL], 1440 ["Skillscrolls" => PICKUP_SKILLSCROLL],
1167 ["Normal Books/Scrolls" => PICKUP_READABLES], 1441 ["Normal Books/Scrolls" => PICKUP_READABLES],
1168 ], 1442 ],
1169 ["Misc", 2, 7, 1443 ["Misc", 2, 5,
1170 ["Food" => PICKUP_FOOD], 1444 ["Food" => PICKUP_FOOD],
1171 ["Drinks" => PICKUP_DRINK], 1445 ["Drinks" => PICKUP_DRINK],
1172 ["Valuables (Money, Gems)" => PICKUP_VALUABLES], 1446 ["Valuables (Money, Gems)" => PICKUP_VALUABLES],
1173 ["Keys" => PICKUP_KEY], 1447 ["Keys" => PICKUP_KEY],
1174 ["Magical Items" => PICKUP_MAGICAL], 1448 ["Magical Items" => PICKUP_MAGICAL],
1175 ["Potions" => PICKUP_POTION], 1449 ["Potions" => PICKUP_POTION],
1176 ["Magic Devices" => PICKUP_MAGIC_DEVICE], 1450 ["Magic Devices" => PICKUP_MAGIC_DEVICE],
1177 ["Ignore cursed" => PICKUP_NOT_CURSED], 1451 ["Ignore cursed" => PICKUP_NOT_CURSED],
1178 ["Jewelery" => PICKUP_JEWELS], 1452 ["Jewelery" => PICKUP_JEWELS],
1453 ["Flesh" => PICKUP_FLESH],
1179 ], 1454 ],
1180 ["Weight/Value ratio", 2, 17] 1455 ["Weight/Value ratio", 2, 17]
1181 ) 1456 )
1182 { 1457 {
1183 my ($title, $x, $y, @bits) = @$_; 1458 my ($title, $x, $y, @bits) = @$_;
1184 $table->add ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]); 1459 $table->add_at ($x, $y, new CFPlus::UI::Label text => $title, align => 1, fg => [1, 1, 0]);
1185 1460
1186 for (@bits) { 1461 for (@bits) {
1187 ++$y; 1462 ++$y;
1188 1463
1189 my $mask = $_->[1]; 1464 my $mask = $_->[1];
1190 $table->add ($x , $y, new CFPlus::UI::Label text => $_->[0], align => 1, expand => 1); 1465 $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 1466 $table->add_at ($x+1, $y, my $checkbox = new CFPlus::UI::CheckBox
1192 state => $::CFG->{pickup} & $mask, 1467 state => $::CFG->{pickup} & $mask,
1193 on_changed => sub { 1468 on_changed => sub {
1194 my ($box, $value) = @_; 1469 my ($box, $value) = @_;
1195 1470
1196 if ($value) { 1471 if ($value) {
1207 1482
1208 ${$_->[2]} = $checkbox if $_->[2]; 1483 ${$_->[2]} = $checkbox if $_->[2];
1209 } 1484 }
1210 } 1485 }
1211 1486
1212 $table->add (2, 18, new CFPlus::UI::ValSlider 1487 $table->add_at (2, 18, new CFPlus::UI::ValSlider
1213 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1], 1488 range => [$::CFG->{pickup} & 0xF, 0, 16, 1, 1],
1214 template => ">= 99", 1489 template => ">= 99",
1215 to_value => sub { ">= " . 5 * $_[0] }, 1490 to_value => sub { ">= " . 5 * $_[0] },
1216 on_changed => sub { 1491 on_changed => sub {
1217 my ($slider, $value) = @_; 1492 my ($slider, $value) = @_;
1220 $::CFG->{pickup} |= int $value 1495 $::CFG->{pickup} |= int $value
1221 if $value; 1496 if $value;
1222 1; 1497 1;
1223 }); 1498 });
1224 1499
1225 $table->add (3, 18, new CFPlus::UI::Button 1500 $table->add_at (3, 18, new CFPlus::UI::Button
1226 text => "set", 1501 text => "set",
1227 on_activate => sub { 1502 on_activate => sub {
1228 $::CONN->send_command ("pickup $::CFG->{pickup}") 1503 $::CONN->send_command ("pickup $::CFG->{pickup}")
1229 if defined $::CONN; 1504 if defined $::CONN;
1230 0 1505 0
1231 }); 1506 });
1232 1507
1233 $table 1508 $r
1234} 1509}
1235 1510
1236my %SORT_ORDER = ( 1511my %SORT_ORDER = (
1237 type => undef, 1512 type => undef,
1238 mtime => sub { sort { 1513 mtime => sub {
1514 my $NOW = time;
1515 sort {
1516 my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6;
1517 my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6;
1518
1239 ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED) 1519 ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1240 or $b->{mtime} <=> $a->{mtime} 1520 or $btime <=> $atime
1241 or $a->{type} <=> $b->{type} 1521 or $a->{type} <=> $b->{type}
1522 } @_
1242 } @_ }, 1523 },
1243 weight => sub { sort { 1524 weight => sub { sort {
1244 $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1) 1525 $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1245 or $a->{type} <=> $b->{type} 1526 or $a->{type} <=> $b->{type}
1246 } @_ }, 1527 } @_ },
1247); 1528);
1272 #TODO# update to weigh/maxweight 1553 #TODO# update to weigh/maxweight
1273 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1); 1554 $hb1->add ($STATWIDS->{i_weight} = new CFPlus::UI::Label align => -1);
1274 1555
1275 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1); 1556 $vb1->add (my $sw1 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1276 $sw1->add ($INV = new CFPlus::UI::Inventory); 1557 $sw1->add ($INV = new CFPlus::UI::Inventory);
1558 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}});
1277 1559
1278 $hb->add (my $vb2 = new CFPlus::UI::VBox); 1560 $hb->add (my $vb2 = new CFPlus::UI::VBox);
1279 1561
1280 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox); 1562 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox);
1281 1563
1310 has_close_button => 1 1592 has_close_button => 1
1311 ; 1593 ;
1312 1594
1313 my $ntb = 1595 my $ntb =
1314 $PL_NOTEBOOK = 1596 $PL_NOTEBOOK =
1315 new CFPlus::UI::Notebook expand => 1, debug => 1; 1597 new CFPlus::UI::Notebook expand => 1;
1316 1598
1317 $ntb->add ( 1599 $ntb->add_tab (
1318 "Statistics (F2)" => $STATS_PAGE = stats_window, 1600 "Statistics (F2)" => $STATS_PAGE = stats_window,
1319 "Shows statistics, where all your Stats and Resistances are shown." 1601 "Shows statistics, where all your Stats and Resistances are shown."
1320 ); 1602 );
1321 $ntb->add ( 1603 $ntb->add_tab (
1322 "Skills (F3)" => $SKILL_PAGE = skill_window, 1604 "Skills (F3)" => $SKILL_PAGE = skill_window,
1323 "Shows all your Skills." 1605 "Shows all your Skills."
1324 ); 1606 );
1325 1607
1326 my $spellsw = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1); 1608 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1327 $spellsw->add ($SPELL_PAGE = new CFPlus::UI::SpellList); 1609 $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList);
1328 $ntb->add ( 1610 $ntb->add_tab (
1329 "Spellbook (F4)" => $spellsw, 1611 "Spellbook (F4)" => $spellsw,
1330 "Displays all spells you have and lets you edit keyboard shortcuts for them." 1612 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1331 ); 1613 );
1332 $ntb->add ( 1614 $ntb->add_tab (
1333 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget, 1615 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1334 "Toggles the inventory window, where you can manage your loot (or treasures :). " 1616 "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." 1617 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1336 ); 1618 );
1619 $ntb->add_tab (Pickup => autopickup_setup,
1620 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1337 1621
1338 $ntb->set_current_page ($INVENTORY_PAGE); 1622 $ntb->set_current_page ($INVENTORY_PAGE);
1339 1623
1340 $plwin->add ($ntb); 1624 $plwin->add ($ntb);
1341 $plwin 1625 $plwin
1342} 1626}
1343 1627
1344sub update_bindings {
1345 $BIND_UPD_CB->() if $BIND_UPD_CB;
1346}
1347
1348sub keyboard_setup { 1628sub keyboard_setup {
1349 my $binding_list = new CFPlus::UI::VBox; 1629 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} 1630}
1462 1631
1463sub help_window { 1632sub help_window {
1464 my $win = new CFPlus::UI::Toplevel 1633 my $win = new CFPlus::UI::Toplevel
1465 x => 'center', 1634 x => 'center',
1622 1791
1623sub show_tip_of_the_day { 1792sub show_tip_of_the_day {
1624 # find all tips 1793 # find all tips
1625 my @tod = CFPlus::Pod::find tip_of_the_day => "*"; 1794 my @tod = CFPlus::Pod::find tip_of_the_day => "*";
1626 1795
1627 my $todindex = $CFPlus::DB_STATE->get ("tip_of_the_day"); 1796 CFPlus::DB::get state => "tip_of_the_day", sub {
1797 my ($todindex) = @_;
1628 $todindex = 0 if $todindex >= @tod; 1798 $todindex = 0 if $todindex >= @tod;
1629 $CFPlus::DB_STATE->put (tip_of_the_day => $todindex + 1); 1799 CFPlus::DB::put state => tip_of_the_day => $todindex + 1, sub { };
1630 1800
1631 # create dialog 1801 # create dialog
1632 my $dialog; 1802 my $dialog;
1633 1803
1634 my $close = sub { 1804 my $close = sub {
1635 $dialog->destroy; 1805 $dialog->destroy;
1806 };
1807
1808 $dialog = new CFPlus::UI::Toplevel
1809 x => "center",
1810 y => "center",
1811 z => 3,
1812 name => 'tip_of_the_day',
1813 force_w => int $WIDTH * 4/9,
1814 force_h => int $WIDTH * 2/9,
1815 title => "Tip of the day #" . (1 + $todindex),
1816 child => my $vbox = new CFPlus::UI::VBox,
1817 has_close_button => 1,
1818 on_delete => $close,
1819 ;
1820
1821 $vbox->add (my $viewer = new CFPlus::UI::TextScroller
1822 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4);
1823 $viewer->add_paragraph (CFPlus::Pod::as_paragraphs CFPlus::Pod::section_of $tod[$todindex]);
1824
1825 $vbox->add (my $table = new CFPlus::UI::Table col_expand => [0, 1]);
1826
1827 $table->add_at (0, 0, new CFPlus::UI::Button
1828 text => "Close",
1829 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>.",
1830 on_activate => $close,
1831 );
1832
1833 $table->add_at (2, 0, new CFPlus::UI::Button
1834 text => "Next",
1835 tooltip => "Show the next <b>Tip of the day</b>.",
1836 on_activate => sub {
1837 $close->();
1838 &show_tip_of_the_day;
1839 },
1840 );
1841
1842 $dialog->show;
1636 }; 1843 };
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} 1844}
1674 1845
1675sub sdl_init { 1846sub sdl_init {
1676 CFPlus::SDL_Init 1847 CFPlus::SDL_Init
1677 and die "SDL::Init failed!\n"; 1848 and die "SDL::Init failed!\n";
1678} 1849}
1679 1850
1680sub video_init { 1851sub video_init {
1681 sdl_init;
1682
1683 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES; 1852 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} >= @SDL_MODES;
1684 1853
1685 my ($old_w, $old_h) = ($WIDTH, $HEIGHT); 1854 my ($old_w, $old_h) = ($WIDTH, $HEIGHT);
1686 1855
1687 ($WIDTH, $HEIGHT) = @{ $SDL_MODES[$CFG->{sdl_mode}] }; 1856 ($WIDTH, $HEIGHT, my ($rgb, $alpha)) = @{ $SDL_MODES[$CFG->{sdl_mode}] };
1688 $FULLSCREEN = $CFG->{fullscreen}; 1857 $FULLSCREEN = $CFG->{fullscreen};
1689 $FAST = $CFG->{fast}; 1858 $FAST = $CFG->{fast};
1690 1859
1691 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $FULLSCREEN 1860 CFPlus::SDL_SetVideoMode $WIDTH, $HEIGHT, $rgb, $alpha, $FULLSCREEN
1692 or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n"; 1861 or die "SDL_SetVideoMode failed: " . (CFPlus::SDL_GetError) . "\n";
1693 1862
1694 $SDL_ACTIVE = 1; 1863 $SDL_ACTIVE = 1;
1695 $LAST_REFRESH = time - 0.01; 1864 $LAST_REFRESH = time - 0.01;
1696 1865
1711 padding => 0, 1880 padding => 0,
1712 z => 100, 1881 z => 100,
1713 force_x => "max", 1882 force_x => "max",
1714 force_y => 0; 1883 force_y => 0;
1715 $DEBUG_STATUS->show; 1884 $DEBUG_STATUS->show;
1716
1717 $BIND_EDITOR = new CFPlus::BindingEditor (x => "max", y => 0);
1718 1885
1719 $STATUSBOX = new CFPlus::UI::Statusbox; 1886 $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]); 1887 $STATUSBOX->add ("Use <b>Alt-Enter</b> to toggle fullscreen mode", timeout => 864000, pri => -100, color => [1, 1, 1, 0.8]);
1721 1888
1722 (new CFPlus::UI::Frame 1889 (new CFPlus::UI::Frame
1740 1907
1741 $MAPWIDGET = new CFPlus::MapWidget; 1908 $MAPWIDGET = new CFPlus::MapWidget;
1742 $MAPWIDGET->connect (activate_console => sub { 1909 $MAPWIDGET->connect (activate_console => sub {
1743 my ($mapwidget, $preset) = @_; 1910 my ($mapwidget, $preset) = @_;
1744 1911
1745 if ($CONSOLE) { 1912 $MESSAGE_WINDOW->activate_console ($preset)
1746 $CONSOLE->{input}->{auto_activated} = 1; 1913 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 }); 1914 });
1754 $MAPWIDGET->show; 1915 $MAPWIDGET->show;
1755 $MAPWIDGET->grab_focus; 1916 $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 1917
1767 $SETUP_DIALOG = new CFPlus::UI::Toplevel 1918 $SETUP_DIALOG = new CFPlus::UI::Toplevel
1768 title => "Setup", 1919 title => "Setup",
1769 name => "setup_dialog", 1920 name => "setup_dialog",
1770 x => 'center', 1921 x => 'center',
1774 force_h => $::HEIGHT * 0.6, 1925 force_h => $::HEIGHT * 0.6,
1775 has_close_button => 1, 1926 has_close_button => 1,
1776 ; 1927 ;
1777 1928
1778 $METASERVER = metaserver_dialog; 1929 $METASERVER = metaserver_dialog;
1930 $MESSAGE_WINDOW = new CFPlus::UI::MessageWindow;
1779 1931
1780 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1, 1932 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1,
1781 filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1); 1933 filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1782 1934
1783 $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup, 1935 $SETUP_NOTEBOOK->add_tab (Server => $SETUP_SERVER = server_setup,
1784 "Configure the server to play on, your username, password and other server-related options."); 1936 "Configure the server to play on, your username, password and other server-related options.");
1785 $SETUP_NOTEBOOK->add (Pickup => autopickup_setup, 1937 $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."); 1938 "Configure various client-specific settings.");
1787 $SETUP_NOTEBOOK->add (Graphics => graphics_setup, 1939 $SETUP_NOTEBOOK->add_tab (Graphics => graphics_setup,
1788 "Configure the video mode, performance, fonts and other graphical aspects of the game."); 1940 "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1789 $SETUP_NOTEBOOK->add (Audio => audio_setup, 1941 $SETUP_NOTEBOOK->add_tab (Audio => audio_setup,
1790 "Configure the use of audio, sound effects and background music."); 1942 "Configure the use of audio, sound effects and background music.");
1791 $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup, 1943 $SETUP_NOTEBOOK->add_tab (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1792 "Lets you define, edit and delete key bindings." 1944 "Lets you define, edit and delete key bindings."
1793 . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor " 1945 . "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 " 1946 . "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. " 1947 . "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 " 1948 . "After pressing the combo the binding will be saved automatically and the "
1797 . "binding editor closes"); 1949 . "binding editor closes");
1798 $SETUP_NOTEBOOK->add (Debug => debug_setup, 1950 $SETUP_NOTEBOOK->add_tab (Debug => debug_setup,
1799 "Some debuggin' options. Do not ask."); 1951 "Some debuggin' options. Do not ask.");
1800 1952
1801 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top 1953 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1802 1954
1803 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG, 1955 $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."); 1956 tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1805 1957
1806 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW = message_window, 1958 $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."); 1959 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1808 1960
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 1961 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 1962
1811 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window, 1963 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window,
1822 }, 1974 },
1823 ); 1975 );
1824 1976
1825 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window, 1977 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Help!", other => $HELP_WINDOW = help_window,
1826 tooltip => "View Documentation"); 1978 tooltip => "View Documentation");
1979
1827 1980
1828 $BUTTONBAR->add (new CFPlus::UI::Button 1981 $BUTTONBAR->add (new CFPlus::UI::Button
1829 text => "Quit", 1982 text => "Quit",
1830 tooltip => "Terminates the program", 1983 tooltip => "Terminates the program",
1831 on_activate => sub { 1984 on_activate => sub {
1849 CFPlus::OpenGL::shutdown; 2002 CFPlus::OpenGL::shutdown;
1850 2003
1851 undef $SDL_ACTIVE; 2004 undef $SDL_ACTIVE;
1852} 2005}
1853 2006
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 {
1874 if ($CFG->{audio_enable}) {
1875 if (open my $fh, "<", CFPlus::find_rcfile "sounds/config") {
1876 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
1877
1878 unless ($SDL_MIXER) {
1879 status "Unable to open sound device: there will be no sound";
1880 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 }
1905 }
1906}
1907
1908sub audio_shutdown {
1909 CFPlus::Mix_CloseAudio if $SDL_MIXER;
1910 undef $SDL_MIXER;
1911 @SOUNDS = ();
1912 %AUDIO_CHUNKS = ();
1913}
1914
1915my %animate_object; 2007my %animate_object;
1916my $animate_timer; 2008my $animate_timer;
1917 2009
1918my $fps = 9; 2010my $fps = 9;
1919
1920my %demo;#d#
1921 2011
1922sub force_refresh { 2012sub force_refresh {
1923 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05; 2013 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05;
1924 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4; 2014 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4;
1925 2015
1926 $CFPlus::UI::ROOT->draw; 2016 $CFPlus::UI::ROOT->draw;
1927 2017
1928 $WANT_REFRESH = 0; 2018 $WANT_REFRESH = 0;
1929 $CAN_REFRESH = 0; 2019 $CAN_REFRESH = 0;
1930 $LAST_REFRESH = $NOW; 2020 $LAST_REFRESH = $NOW;
1931 2021
1932 CFPlus::SDL_GL_SwapBuffers; 2022 CFPlus::SDL_GL_SwapBuffers;
1933} 2023}
1934 2024
1935my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub { 2025my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub {
1936 $NOW = time; 2026 $NOW = time;
1937 2027
1938 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_) 2028 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1939 for CFPlus::SDL_PollEvent; 2029 for CFPlus::poll_events;
1940 2030
1941 if (%animate_object) { 2031 if (%animate_object) {
1942 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object; 2032 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1943 $WANT_REFRESH++; 2033 ++$WANT_REFRESH;
1944 } 2034 }
1945 2035
1946 if ($WANT_REFRESH) { 2036 if ($WANT_REFRESH) {
1947 force_refresh; 2037 force_refresh;
1948 } else { 2038 } else {
1958sub animation_stop { 2048sub animation_stop {
1959 my ($widget) = @_; 2049 my ($widget) = @_;
1960 delete $animate_object{$widget}; 2050 delete $animate_object{$widget};
1961} 2051}
1962 2052
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 = ( 2053%SDL_CB = (
1973 CFPlus::SDL_QUIT => sub { 2054 CFPlus::SDL_QUIT => sub {
1974 Event::unloop -1; 2055 exit;
1975 }, 2056 },
1976 CFPlus::SDL_VIDEORESIZE => sub { 2057 CFPlus::SDL_VIDEORESIZE => sub {
1977 }, 2058 },
1978 CFPlus::SDL_VIDEOEXPOSE => sub { 2059 CFPlus::SDL_VIDEOEXPOSE => sub {
1979 CFPlus::UI::full_refresh; 2060 CFPlus::UI::full_refresh;
1980 }, 2061 },
1981 CFPlus::SDL_ACTIVEEVENT => sub { 2062 CFPlus::SDL_ACTIVEEVENT => sub {
1982# printf "active %x %x\n", $SDL_EV->active_gain, $SDL_EV->active_state;#d# 2063# not useful, as APPACTIVE include sonly iconified state, not unmapped
2064# printf "active %x %x %x\n", $_[0]{gain}, $_[0]{state}, CFPlus::SDL_GetAppState;#d#
2065# printf "a %x\n", CFPlus::SDL_GetAppState & CFPlus::SDL_APPACTIVE;#d#
2066# printf "A\n" if $_[0]{state} & CFPlus::SDL_APPACTIVE;
2067# printf "K\n" if $_[0]{state} & CFPlus::SDL_APPINPUTFOCUS;
2068# printf "M\n" if $_[0]{state} & CFPlus::SDL_APPMOUSEFOCUS;
1983 }, 2069 },
1984 CFPlus::SDL_KEYDOWN => sub { 2070 CFPlus::SDL_KEYDOWN => sub {
1985 if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) { 2071 if ($_[0]{mod} & CFPlus::KMOD_ALT && $_[0]{sym} == 13) {
1986 # alt-enter 2072 # alt-enter
1987 $FULLSCREEN_ENABLE->toggle; 2073 $FULLSCREEN_ENABLE->toggle;
2008 2094
2009$SIG{INT} = $SIG{TERM} = sub { exit }; 2095$SIG{INT} = $SIG{TERM} = sub { exit };
2010 2096
2011{ 2097{
2012 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc"; 2098 CFPlus::read_cfg "$Crossfire::VARDIR/cfplusrc";
2099 CFPlus::DB::Server::run;
2100
2013 CFPlus::UI::set_layout ($::CFG->{layout}); 2101 CFPlus::UI::set_layout ($::CFG->{layout});
2014 2102
2015 my %DEF_CFG = ( 2103 my %DEF_CFG = (
2016 sdl_mode => 0, 2104 sdl_mode => 0,
2017 width => 640,
2018 height => 480,
2019 fullscreen => 0, 2105 fullscreen => 0,
2020 fast => 0, 2106 fast => 0,
2021 map_scale => 1, 2107 map_scale => 1,
2022 fow_enable => 1, 2108 fow_enable => 1,
2023 fow_intensity => 0.45, 2109 fow_intensity => 0,
2024 fow_smooth => 0, 2110 map_smoothing => 1,
2025 gui_fontsize => 1, 2111 gui_fontsize => 1,
2026 log_fontsize => 0.7, 2112 log_fontsize => 0.7,
2027 gauge_fontsize => 1, 2113 gauge_fontsize => 1,
2028 gauge_size => 0.35, 2114 gauge_size => 0.35,
2029 stat_fontsize => 0.7, 2115 stat_fontsize => 0.7,
2030 mapsize => 100, 2116 mapsize => 100,
2031 say_command => 'say',
2032 audio_enable => 1, 2117 audio_enable => 1,
2033 bgm_enable => 1, 2118 bgm_enable => 1,
2034 bgm_volume => 0.25, 2119 bgm_volume => 0.25,
2035 face_prefetch => 0,
2036 output_sync => 1, 2120 output_sync => 1,
2037 output_count => 1, 2121 output_count => 1,
2122 output_rate => "",
2038 pickup => 0, 2123 pickup => 0,
2039 inv_sort => "mtime", 2124 inv_sort => "mtime",
2040 default => "profile", # default profile 2125 default => "profile", # default profile
2041 show_tips => 1, 2126 show_tips => 1,
2127 logview_max_par => 1000,
2042 ); 2128 );
2043 2129
2044 while (my ($k, $v) = each %DEF_CFG) { 2130 while (my ($k, $v) = each %DEF_CFG) {
2045 $CFG->{$k} = $v unless exists $CFG->{$k}; 2131 $CFG->{$k} = $v unless exists $CFG->{$k};
2046 } 2132 }
2047 2133
2048 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de"; 2134 $CFG->{profile}{default}{host} ||= "crossfire.schmorp.de";
2135 $PROFILE = $CFG->{profile}{default};
2136
2137 # convert old bindings (only default profile matters)
2138 if (my $bindings = delete $PROFILE->{bindings}) {
2139 while (my ($mod, $syms) = each %$bindings) {
2140 while (my ($sym, $cmds) = each %$syms) {
2141 push @{ $PROFILE->{macro} }, {
2142 accelkey => [$mod*1, $sym*1],
2143 action => $cmds,
2144 };
2145 }
2146 }
2147 }
2049 2148
2050 sdl_init; 2149 sdl_init;
2051 2150
2052 @SDL_MODES = reverse 2151 @SDL_MODES = CFPlus::SDL_ListModes 8, 8;
2053 grep $_->[0] >= 640 && $_->[1] >= 480, 2152 @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)"; 2153 @SDL_MODES or CFPlus::fatal "Unable to find a usable video mode\n(hardware accelerated opengl fullscreen)";
2154
2155 @SDL_MODES = sort { $a->[0] * $a->[1] <=> $b->[0] * $b->[1] } @SDL_MODES;
2057 2156
2058 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES; 2157 $CFG->{sdl_mode} = 0 if $CFG->{sdl_mode} > @SDL_MODES;
2059 2158
2060 { 2159 {
2061 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw( 2160 my @fonts = map CFPlus::find_rcfile "fonts/$_", qw(
2090# } 2189# }
2091# my $t2 = Time::HiRes::time; 2190# my $t2 = Time::HiRes::time;
2092# warn $t2-$t1; 2191# warn $t2-$t1;
2093# } 2192# }
2094 2193
2194 $startup_done->();
2195
2095 video_init; 2196 video_init;
2096 audio_init; 2197 audio_init;
2097} 2198}
2098 2199
2099show_tip_of_the_day if $CFG->{show_tips}; 2200show_tip_of_the_day if $CFG->{show_tips};
2100
2101use Data::Dumper; warn Dumper [CFPlus::win32_proxy_info()];#d#
2102 2201
2103Event::loop; 2202Event::loop;
2104#CFPlus::SDL_Quit; 2203#CFPlus::SDL_Quit;
2105#CFPlus::_exit 0; 2204#CFPlus::_exit 0;
2106 2205
2206END {
2107END { CFPlus::SDL_Quit } 2207 CFPlus::SDL_Quit;
2208 CFPlus::DB::Server::stop;
2209}
2108 2210
2109=head1 NAME 2211=head1 NAME
2110 2212
2111cfplus - A Crossfire+ and Crossfire game client 2213cfplus - A Crossfire+ and Crossfire game client
2112 2214

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines