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.190 by root, Sun Jul 29 18:56:03 2007 UTC vs.
Revision 1.209 by root, Sat Aug 11 14:41:38 2007 UTC

71unshift @INC, $ENV{PAR_TEMP} 71unshift @INC, $ENV{PAR_TEMP}
72 if %PAR::LibCache; 72 if %PAR::LibCache;
73 73
74use Time::HiRes 'time'; 74use Time::HiRes 'time';
75use Event; 75use Event;
76use List::Util qw(max min);
76 77
77use Crossfire; 78use Crossfire;
78use Crossfire::Protocol::Constants; 79use Crossfire::Protocol::Constants;
79 80
80use Compress::LZF; 81use Compress::LZF;
112our $CONN; 113our $CONN;
113our $PROFILE; # current profile 114our $PROFILE; # current profile
114our $FAST; # fast, low-quality mode, possibly useful for software-rendering 115our $FAST; # fast, low-quality mode, possibly useful for software-rendering
115 116
116our $WANT_REFRESH; 117our $WANT_REFRESH;
117our $CAN_REFRESH;
118 118
119our @SDL_MODES; 119our @SDL_MODES;
120our $WIDTH; 120our $WIDTH;
121our $HEIGHT; 121our $HEIGHT;
122our $FULLSCREEN; 122our $FULLSCREEN;
143our $SETUP_KEYBOARD; 143our $SETUP_KEYBOARD;
144 144
145our $PL_NOTEBOOK; 145our $PL_NOTEBOOK;
146our $PL_WINDOW; 146our $PL_WINDOW;
147 147
148our $MUSIC_PLAYING_WIDGET;
149our $LICENSE_WIDGET;
150
148our $INVENTORY_PAGE; 151our $INVENTORY_PAGE;
149our $STATS_PAGE; 152our $STATS_PAGE;
150our $SKILL_PAGE; 153our $SKILL_PAGE;
151our $SPELL_PAGE; 154our $SPELL_PAGE;
152our $SPELL_LIST; 155our $SPELL_LIST;
158our $STATWIDS; 161our $STATWIDS;
159 162
160our $SDL_ACTIVE; 163our $SDL_ACTIVE;
161our %SDL_CB; 164our %SDL_CB;
162 165
166our $ALT_ENTER_MESSAGE;
167our $STATUSBOX;
168our $DEBUG_STATUS;
169
170our $INV;
171our $INVR;
172our $INV_RIGHT_HB;
173
174our $PICKUP_CFG;
175
176#############################################################################
177
178sub status {
179 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
180}
181
182sub debug {
183 $DEBUG_STATUS->set_text ($_[0]);
184}
185
186sub message {
187 my ($para) = @_;
188 $MESSAGE_WINDOW->message ($para);
189}
190
191#############################################################################
192#TODO: maybe move into own audio module...
193
163our $SDL_MIXER; 194our $SDL_MIXER;
195
164our $MUSIC_DEFAULT = "in_a_heartbeat.ogg"; 196our $MUSIC_DEFAULT = "in_a_heartbeat.ogg";
165our @MUSIC_WANT; 197our $MUSIC_WANT; # arryref of ambient music we want to play
198our @MUSIC_HAVE; # ambient music we have on disk
166our $MUSIC_START; 199our $MUSIC_START;
200our @MUSIC_JINGLE; # which jingles to play next
167our $MUSIC_PLAYING_DATA; 201our $MUSIC_PLAYING_DATA;
168our $MUSIC_PLAYING_META; 202our $MUSIC_PLAYING_META;
169our $MUSIC_PLAYER; 203our $MUSIC_PLAYER;
170our $MUSIC_RESUME = 30; # resume music when players less than these many seconds before 204our $MUSIC_RESUME = 30; # resume music when played less than these many seconds before
171our @SOUNDS; # event => file mapping 205
172our %AUDIO_CHUNKS; # audio files 206our %AUDIO_CHUNK; # audio "files"
207our %AUDIO_PLAY; # which audio faces should be played
173 208
174our $ALT_ENTER_MESSAGE; 209sub audio_channel_finished {
175our $STATUSBOX; 210 my ($channel) = @_;
176our $DEBUG_STATUS;
177 211
178our $INV; 212# warn "channel $channel finished\n";#d#
179our $INVR;
180our $INV_RIGHT_HB;
181
182our $PICKUP_CFG;
183
184sub status {
185 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
186} 213}
187 214
188sub debug { 215sub audio_sound_push($) {
189 $DEBUG_STATUS->set_text ($_[0]);
190}
191
192sub message {
193 my ($para) = @_; 216 my ($face) = @_;
194 $MESSAGE_WINDOW->message ($para); 217
218 $CFG->{effects_enable}
219 or return;
220
221 $AUDIO_PLAY{$face}
222 or return;
223
224 if (my $chunk = $AUDIO_CHUNK{$face}) {
225 for (grep $_->[0] >= Event::time, @{(delete $AUDIO_PLAY{$face}) || []}) {
226 my (undef, $dx, $dy, $vol) = @$_;
227
228 my $channel = CFPlus::Channel::find;
229 $channel->volume ($vol * $CFG->{effects_volume} * 128 / 255);
230 $channel->set_position_r ($dx, $dy, 20);
231 $chunk->play ($channel);
232 }
233 } else {
234 # sound_meta not set means data is in flight either way
235 my $meta = $CONN->{sound_meta}{$face}
236 or return;
237
238 # if its a jingle, play it as ambient music
239 if ($meta->{meta}{jingle}) {
240 if (delete $AUDIO_PLAY{$face}) { # take the jingle out of the sound queue
241 push @MUSIC_JINGLE, $meta; # push it oto the music/jingle queue
242 &audio_music_push ($face);
243 }
244 } else {
245 # fetch from database
246 CFPlus::DB::get res_data => $meta->{name}, sub {
247 my $rwops = new CFPlus::RW $_[0];
248 my $chunk = new CFPlus::MixChunk $rwops
249 or Carp::confess "sound face " . (JSON::XS::to_json $meta) . " unloadable: " . CFPlus::Mix_GetError;
250 $chunk->volume (($meta->{meta}{volume} || 1) * 128);
251 $AUDIO_CHUNK{$face} = $chunk;
252
253 audio_sound_push ($face);
254 };
255 }
256 }
195} 257}
258
259sub audio_sound_play {
260 my ($face, $dx, $dy, $vol) = @_;
261
262 $SDL_MIXER
263 or return;
264 $CFG->{effects_enable}
265 or return;
266
267 my $queue = $AUDIO_PLAY{$face} ||= [];
268 push @$queue, [Event::time + 0.2, $dx, $dy, $vol]; # delay sound by max. 0.2s
269 audio_sound_push $face
270 unless @$queue > 1;
271}
272
273sub audio_music_set_meta {
274 my ($meta) = @_;
275
276 $MUSIC_PLAYING_META = $meta;
277 $MUSIC_PLAYING_WIDGET->set_markup (
278 "<b>Name</b>: " . (CFPlus::asxml $meta->{meta}{name}) . "\n"
279 . "<b>Author</b>: " . (CFPlus::asxml $meta->{meta}{author}) . "\n"
280 . "<b>Source</b>: " . (CFPlus::asxml $meta->{meta}{source}) . "\n"
281 . "<b>License</b>: " . (CFPlus::asxml $meta->{meta}{license})
282 );
283}
284
285sub audio_music_update_volume {
286 return unless $MUSIC_PLAYING_META;
287 my $volume = $MUSIC_PLAYING_META->{meta}{volume} || 1;
288 my $base = $MUSIC_PLAYING_META->{meta}{jingle} ? 1 : $CFG->{bgm_volume};
289 CFPlus::MixMusic::volume $base * $volume * 128;
290}
291
292sub audio_music_start {
293 my $meta = $MUSIC_PLAYING_META;
294
295 CFPlus::DB::get res_data => $meta->{name}, sub {
296 return unless $SDL_MIXER;
297
298 # music might have changed...
299 $meta eq $MUSIC_PLAYING_META
300 or return &audio_music_start ();
301
302 audio_music_update_volume;
303
304 $MUSIC_PLAYING_DATA = \$_[0];
305
306 my $rwops = $meta->{path}
307 ? new_from_file CFPlus::RW $meta->{path}
308 : new CFPlus::RW $$MUSIC_PLAYING_DATA;
309
310 $MUSIC_PLAYER = new CFPlus::MixMusic $rwops
311 or Carp::confess "music face $meta->{face} unloadable: " . CFPlus::Mix_GetError;
312
313 my $NOW = time;
314
315 if ($MUSIC_PLAYING_META->{stop_time} > $NOW - $MUSIC_RESUME) {
316 my $pos = $MUSIC_PLAYING_META->{stop_pos};
317 $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
318 $MUSIC_START = time - $pos;
319 } else {
320 $MUSIC_PLAYER->play (0);
321 $MUSIC_START = time;
322 }
323
324 delete $meta->{stop_time};
325 delete $meta->{stop_pos};
326 }
327}
328
329sub audio_music_push {
330 return unless $SDL_MIXER;
331
332 my $fade_out;
333
334 if (@MUSIC_JINGLE) {
335 @MUSIC_HAVE = $MUSIC_JINGLE[0];
336 $fade_out = 333;
337 } else {
338 return unless $CFG->{bgm_enable};
339
340 my @have =
341 grep $_,
342 map $CONN->{music_meta}{$_},
343 @$MUSIC_WANT;
344
345 @MUSIC_HAVE = @have
346 if @have;
347
348 # default MUSIC_HAVE == MUSIC_DEFAULT
349 @MUSIC_HAVE = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_HAVE;
350 $fade_out = 1000;
351 }
352
353 # if the currently playing song is acceptable, let it continue
354 return if grep $MUSIC_PLAYING_META == $_, @MUSIC_HAVE;
355
356 my $NOW = time;
357
358 if ($MUSIC_PLAYING_META) {
359 $MUSIC_PLAYING_META->{stop_time} = $NOW;
360 $MUSIC_PLAYING_META->{stop_pos} = $NOW - $MUSIC_START;
361 CFPlus::MixMusic::fade_out $fade_out;
362 } else {
363 # sort by stop time, oldest first
364 @MUSIC_HAVE = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_HAVE;
365
366 # if the most recently-played piece played very recently,
367 # resume it, else choose the oldest piece for rotation.
368 audio_music_set_meta
369 $MUSIC_HAVE[-1]{stop_time} > $NOW - $MUSIC_RESUME
370 ? $MUSIC_HAVE[-1]
371 : $MUSIC_HAVE[0];
372
373 audio_music_start;
374 }
375}
376
377sub audio_music_set_ambient {
378 my ($songs) = @_;
379
380 $MUSIC_WANT = $songs;
381 audio_music_push;
382}
383
384sub audio_music_finished {
385 # we compress multiple jingles of the same type
386 shift @MUSIC_JINGLE
387 while @MUSIC_JINGLE && $MUSIC_PLAYING_META == $MUSIC_JINGLE[0];
388
389 $MUSIC_PLAYING_WIDGET->clear;
390
391 undef $MUSIC_PLAYER;
392 undef $MUSIC_PLAYING_META;
393 undef $MUSIC_PLAYING_DATA;
394
395 audio_music_push;
396}
397
398sub audio_init {
399 if ($CFG->{audio_enable}) {
400 $ENV{MIX_EFFECTSMAXSPEED} = 1;
401 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
402
403 unless ($SDL_MIXER) {
404 status "Unable to open sound device: there will be no sound";
405 return;
406 }
407
408 CFPlus::Mix_AllocateChannels 16;
409
410 audio_music_finished;
411 } else {
412 undef $SDL_MIXER;
413 }
414}
415
416sub audio_shutdown {
417 undef $MUSIC_PLAYER;
418 undef $MUSIC_PLAYING_META;
419 undef $MUSIC_PLAYING_DATA;
420
421 $MUSIC_WANT = [];
422 @MUSIC_JINGLE = ();
423 %AUDIO_PLAY = ();
424 %AUDIO_CHUNK = ();
425
426 CFPlus::Mix_CloseAudio if $SDL_MIXER;
427 undef $SDL_MIXER;
428}
429
430#############################################################################
196 431
197sub destroy_query_dialog { 432sub destroy_query_dialog {
198 (delete $_[0]{query_dialog})->destroy 433 (delete $_[0]{query_dialog})->destroy
199 if $_[0]{query_dialog}; 434 if $_[0]{query_dialog};
200} 435}
460 query => \&server_query, 695 query => \&server_query,
461 696
462 setup_req => { 697 setup_req => {
463 smoothing => $CFG->{map_smoothing}*1, 698 smoothing => $CFG->{map_smoothing}*1,
464 }, 699 },
465
466 sound_play => sub {
467 my ($x, $y, $soundnum, $type) = @_;
468
469 $SDL_MIXER
470 or return;
471
472 my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
473 or return;
474
475 $chunk->play;
476 },
477 }; 700 };
478 701
479 if ($CONN) { 702 if ($CONN) {
480 CFPlus::lowdelay fileno $CONN->{fh}; 703 CFPlus::lowdelay fileno $CONN->{fh};
481 704
492 $SETUP_DIALOG->show; 715 $SETUP_DIALOG->show;
493 $PL_WINDOW->hide; 716 $PL_WINDOW->hide;
494 $SPELL_LIST->clear_spells; 717 $SPELL_LIST->clear_spells;
495 $CFPlus::UI::ROOT->emit (stop_game => ! ! $CONN); 718 $CFPlus::UI::ROOT->emit (stop_game => ! ! $CONN);
496 719
497 &audio_music_set ([]); 720 &audio_music_set_ambient ([]);
498 721
499 return unless $CONN; 722 return unless $CONN;
500 723
501 status "connection closed"; 724 status "connection closed";
502 725
581 804
582 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing"); 805 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing");
583 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 806 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
584 state => $CFG->{map_smoothing}, 807 state => $CFG->{map_smoothing},
585 tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. " 808 tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. "
586 . "This increases load on the graphics subsystem and works only with 2.x servers. " 809 . "This increases load on the graphics subsystem and works only with TRT servers. "
587 . "Changes take effect at next connection only.", 810 . "Changes take effect at next connection only.",
588 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 } 811 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 }
589 ); 812 );
590 813
591 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War"); 814 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War");
635} 858}
636 859
637sub audio_setup { 860sub audio_setup {
638 my $vbox = new CFPlus::UI::VBox; 861 my $vbox = new CFPlus::UI::VBox;
639 862
640 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]); 863 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 0, 1]);
641 864
642 my $row = 0; 865 my $row = 0;
643 866
644 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable"); 867 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable");
645 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 868 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
649 ); 872 );
650# $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume"); 873# $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume");
651# $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub { 874# $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
652# $CFG->{effects_volume} = $_[1]; 875# $CFG->{effects_volume} = $_[1];
653# }); 876# });
877
878 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Sound Effects");
879 $table->add_at (1, $row, new CFPlus::UI::CheckBox
880 expand => 1, state => $CFG->{effects_enable},
881 tooltip => "If enabled, sound effects are enabled. If disabled, no sound effects will be played.",
882 on_changed => sub {
883 $CFG->{effects_enable} = $_[1];
884 $CONN->update_fx_want if $CONN;
885 0
886 }
887 );
888 $table->add_at (2, $row++, new CFPlus::UI::Slider
889 expand => 1, range => [$CFG->{effects_volume}, 0, 1, 0, 1/128],
890 tooltip => "The relative volume of sound effects. Best audio quality is achieved if this "
891 . "is set highest (rightmost) and you use your operating system volume setting. Changes are instant.",
892 on_changed => sub { $CFG->{effects_volume} = $_[1]; 0 }
893 );
894
654 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music"); 895 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music");
655 $table->add_at (1, $row++, my $hbox = new CFPlus::UI::HBox); 896 $table->add_at (1, $row, new CFPlus::UI::CheckBox
656 $hbox->add (new CFPlus::UI::CheckBox
657 expand => 1, state => $CFG->{bgm_enable}, 897 expand => 1, state => $CFG->{bgm_enable},
658 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.", 898 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
659 on_changed => sub { $CFG->{bgm_enable} = $_[1]; 0 } 899 on_changed => sub {
900 $CFG->{bgm_enable} = $_[1];
901 $CONN->update_fx_want if $CONN;
902 0
903 }
660 ); 904 );
661 $hbox->add (new CFPlus::UI::Slider 905 $table->add_at (2, $row++, new CFPlus::UI::Slider
662 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128], 906 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
663 tooltip => "The volume of the background music. Changes are instant.", 907 tooltip => "The volume of the background music. Changes are instant.",
664 on_changed => sub { $CFG->{bgm_volume} = $_[1]; CFPlus::MixMusic::volume $_[1] * 128; 0 } 908 on_changed => sub { $CFG->{bgm_volume} = $_[1]; audio_music_update_volume; 0 }
665 ); 909 );
666 910
667 $table->add_at (1, $row++, new CFPlus::UI::Button 911 $table->add_at (1, $row++, new CFPlus::UI::Button
668 expand => 1, align => 0, text => "Apply", 912 c_colspan => 2, expand => 1, align => 0, text => "Apply",
669 tooltip => "Apply the audio settings", 913 tooltip => "Apply the audio settings",
670 on_activate => sub { 914 on_activate => sub {
671 audio_shutdown (); 915 audio_shutdown ();
672 audio_init (); 916 audio_init ();
673 0 917 0
744 $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } ); 988 $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } );
745 989
746 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d# 990 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d#
747 991
748 $table->add_at (7,7, my $t = new CFPlus::UI::Table expand => 0); 992 $table->add_at (7,7, my $t = new CFPlus::UI::Table expand => 0);
749 $t->add_at (0,0, new CFPlus::UI::Label text => "a a a a", rowspan => 1, colspan => 2); 993 $t->add_at (0,0, new CFPlus::UI::Label text => "a a a a", c_rowspan => 1, c_colspan => 2);
750 $t->add_at (2,0, new CFPlus::UI::Label text => "b\nb", rowspan => 2, colspan => 1); 994 $t->add_at (2,0, new CFPlus::UI::Label text => "b\nb", c_rowspan => 2, c_colspan => 1);
751 $t->add_at (1,2, new CFPlus::UI::Label text => "c c c c", rowspan => 1, colspan => 2); 995 $t->add_at (1,2, new CFPlus::UI::Label text => "c c c c", c_rowspan => 1, c_colspan => 2);
752 $t->add_at (0,1, new CFPlus::UI::Label text => "d\nd", rowspan => 2, colspan => 1); 996 $t->add_at (0,1, new CFPlus::UI::Label text => "d\nd", c_rowspan => 2, c_colspan => 1);
753 $t->add_at (1,1, new CFPlus::UI::Label text => "e"); 997 $t->add_at (1,1, new CFPlus::UI::Label text => "e");
754 998
755 $table->add_at (7, 6, my $c = new CFPlus::UI::Canvas); 999 $table->add_at (7, 6, my $c = new CFPlus::UI::Canvas);
756 1000
757 $c->add_items ({ 1001 $c->add_items ({
1082 text => $CFG->{profile}{default}{host}, 1326 text => $CFG->{profile}{default}{host},
1083 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to", 1327 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
1084 on_changed => sub { 1328 on_changed => sub {
1085 my ($self, $value) = @_; 1329 my ($self, $value) = @_;
1086 $CFG->{profile}{default}{host} = $value; 1330 $CFG->{profile}{default}{host} = $value;
1087 0 1331 1
1088 } 1332 }
1089 ); 1333 );
1090 1334
1091 $vbox->add (new CFPlus::UI::Button 1335 $vbox->add (new CFPlus::UI::Button
1092 expand => 1, 1336 expand => 1,
1093 text => "Server List", 1337 text => "Server List",
1094 other => $METASERVER, 1338 other => $METASERVER,
1095 tooltip => "Show a list of available crossfire servers", 1339 tooltip => "Show a list of available crossfire servers",
1096 on_activate => sub { $METASERVER->toggle_visibility; 0 }, 1340 on_activate => sub { $METASERVER->toggle_visibility; 0 },
1097 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 0 }, 1341 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 1 },
1098 ); 1342 );
1099 } 1343 }
1100 1344
1101 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username"); 1345 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username");
1102 $table->add_at (1, 4, new CFPlus::UI::Entry 1346 $table->add_at (1, 4, new CFPlus::UI::Entry
1103 text => $CFG->{profile}{default}{user}, 1347 text => $CFG->{profile}{default}{user},
1104 tooltip => "The name of your character on the server", 1348 tooltip => "The name of your character on the server",
1105 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value } 1349 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value; 1 }
1106 ); 1350 );
1107 1351
1108 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password"); 1352 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password");
1109 $table->add_at (1, 5, new CFPlus::UI::Entry 1353 $table->add_at (1, 5, new CFPlus::UI::Entry
1110 text => $CFG->{profile}{default}{password}, 1354 text => $CFG->{profile}{default}{password},
1111 hidden => 1, 1355 hidden => 1,
1112 tooltip => "The password for your character", 1356 tooltip => "The password for your character",
1113 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value } 1357 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value; 1 }
1114 ); 1358 );
1115 1359
1116 $table->add_at (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size"); 1360 $table->add_at (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size");
1117 $table->add_at (1, 7, new CFPlus::UI::Slider 1361 $table->add_at (1, 7, new CFPlus::UI::Slider
1118 force_w => 100, 1362 force_w => 100,
1119 range => [$CFG->{mapsize}, 10, 100, 0, 1], 1363 range => [$CFG->{mapsize}, 10, 100, 0, 1],
1120 tooltip => "This is the size of the portion of the map update the server sends you. " 1364 tooltip => "This is the size of the portion of the map update the server sends you. "
1121 . "If you set this to a high value you will be able to see further, " 1365 . "If you set this to a high value you will be able to see further, "
1122 . "but you also increase bandwidth requirements and latency. " 1366 . "but you also increase bandwidth requirements and latency. "
1123 . "This option is only used once at log-in.", 1367 . "This option is only used once at log-in.",
1124 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 0 }, 1368 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 1 },
1125 ); 1369 );
1126 1370
1127 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate"); 1371 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate");
1128 $table->add_at (1, 8, new CFPlus::UI::Entry 1372 $table->add_at (1, 8, new CFPlus::UI::Entry
1129 text => $CFG->{output_rate}, 1373 text => $CFG->{output_rate},
1130 tooltip => "The approximate bandwidth in bytes per second that the server should not exceed " 1374 tooltip => "The maximum bandwidth in bytes per second that the server should not exceed "
1131 . "when sending images, to ensure interactiveness. When 0 or unset, the server " 1375 . "when sending data. When 0 or unset, the server "
1132 . "default will be used, which is usually around 100kb/s.", 1376 . "default will be used, which is usually around 100kb/s. Most servers will "
1377 . "dynamically find an optimal rate, so adjust this only when necessary.",
1133 on_changed => sub { $CFG->{output_rate} = $_[1]; 0 }, 1378 on_changed => sub { $CFG->{output_rate} = $_[1]; 1 },
1134 ); 1379 );
1135 1380
1136 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count"); 1381 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1137 $table->add_at (1, 9, new CFPlus::UI::Entry 1382 $table->add_at (1, 9, new CFPlus::UI::Entry
1138 text => $CFG->{output_count}, 1383 text => $CFG->{output_count},
1139 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.", 1384 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1140 on_changed => sub { $CFG->{output_count} = $_[1]; 0 }, 1385 on_changed => sub { $CFG->{output_count} = $_[1]; 1 },
1141 ); 1386 );
1142 1387
1143 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync"); 1388 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync");
1144 $table->add_at (1, 10, new CFPlus::UI::Entry 1389 $table->add_at (1, 10, new CFPlus::UI::Entry
1145 text => $CFG->{output_sync}, 1390 text => $CFG->{output_sync},
1146 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.", 1391 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1147 on_changed => sub { $CFG->{output_sync} = $_[1]; 0 }, 1392 on_changed => sub { $CFG->{output_sync} = $_[1]; 1 },
1148 ); 1393 );
1149 1394
1150 $table->add_at (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button 1395 $table->add_at (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button
1151 expand => 1, 1396 expand => 1,
1152 align => 0, 1397 align => 0,
1153 text => "Login", 1398 text => "Login",
1154 on_activate => sub { 1399 on_activate => sub {
1155 $CONN ? stop_game 1400 $CONN ? stop_game
1156 : start_game; 1401 : start_game;
1157 0 1402 1
1158 }, 1403 },
1159 ); 1404 );
1160 1405
1161 $vbox->add (new CFPlus::UI::FancyFrame 1406 $vbox->add (new CFPlus::UI::FancyFrame
1162 label => "Server Info", 1407 label => "Server Info",
1363 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor"); 1608 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor");
1364 1609
1365 $hb 1610 $hb
1366} 1611}
1367 1612
1613sub media_window {
1614 my $vb = new CFPlus::UI::VBox;
1615
1616 $vb->add (new CFPlus::UI::FancyFrame
1617 label => "Currently playing music",
1618 child => new CFPlus::UI::ScrolledWindow scroll_x => 1, scroll_y => 0,
1619 child => ($MUSIC_PLAYING_WIDGET = new CFPlus::UI::Label ellipsise => 0, fontsize => 0.8),
1620 );
1621
1622 $vb->add (new CFPlus::UI::FancyFrame
1623 label => "Other media used in this session",
1624 expand => 1,
1625 child => ($LICENSE_WIDGET = new CFPlus::UI::TextScroller
1626 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4),
1627 );
1628
1629 $vb
1630}
1631
1632sub add_license {
1633 my ($meta) = @_;
1634
1635 $meta = $meta->{meta}
1636 or return;
1637
1638 $meta->{license} || $meta->{author} || $meta->{source}
1639 or return;
1640
1641 $LICENSE_WIDGET->add_paragraph ({
1642 fg => [1, 1, 1, 1],
1643 markup => "<small>"
1644 . "<b>Name:</b> " . (CFPlus::asxml $meta->{name}) . "\n"
1645 . "<b>Author:</b> " . (CFPlus::asxml $meta->{author}) . "\n"
1646 . "<b>Source:</b> " . (CFPlus::asxml $meta->{source}) . "\n"
1647 . "<b>License:</b> " . (CFPlus::asxml $meta->{license}) . "\n"
1648 . "</small>",
1649 });
1650 $LICENSE_WIDGET->scroll_to_bottom;
1651}
1652
1368sub toggle_player_page { 1653sub toggle_player_page {
1369 my ($widget) = @_; 1654 my ($widget) = @_;
1370 1655
1371 if ($PL_WINDOW->{visible} && $PL_NOTEBOOK->get_current_page == $widget) { 1656 if ($PL_WINDOW->{visible} && $PL_NOTEBOOK->get_current_page == $widget) {
1372 $PL_WINDOW->hide; 1657 $PL_WINDOW->hide;
1409 $ntb->add_tab ( 1694 $ntb->add_tab (
1410 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget, 1695 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1411 "Toggles the inventory window, where you can manage your loot (or treasures :). " 1696 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1412 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory." 1697 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1413 ); 1698 );
1414 $ntb->add_tab (Pickup => autopickup_setup, 1699 $ntb->add_tab (Pickup => autopickup_setup,
1415 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them."); 1700 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1701
1702 $ntb->add_tab (Media => media_window,
1703 "License, Author and Source info for media sent by the server.");
1416 1704
1417 $ntb->set_current_page ($INVENTORY_PAGE); 1705 $ntb->set_current_page ($INVENTORY_PAGE);
1418 1706
1419 $plwin->add ($ntb); 1707 $plwin->add ($ntb);
1420 $plwin 1708 $plwin
1797 CFPlus::OpenGL::shutdown; 2085 CFPlus::OpenGL::shutdown;
1798 2086
1799 undef $SDL_ACTIVE; 2087 undef $SDL_ACTIVE;
1800} 2088}
1801 2089
1802sub audio_channel_finished {
1803 my ($channel) = @_;
1804
1805 #warn "channel $channel finished\n";#d#
1806}
1807
1808sub audio_music_set {
1809 my ($songs) = @_;
1810
1811 my @want =
1812 grep $_,
1813 map $CONN->{music_meta}{$_},
1814 @$songs;
1815
1816 if (@want) {
1817 @MUSIC_WANT = @want;
1818 &audio_music_changed ();
1819 }
1820}
1821
1822sub audio_music_start {
1823 my $meta = $MUSIC_PLAYING_META;
1824
1825 CFPlus::DB::get res_data => $meta->{name}, sub {
1826 return unless $SDL_MIXER;
1827
1828 # music might have changed...
1829 $meta eq $MUSIC_PLAYING_META
1830 or return &audio_music_start ();
1831
1832 $MUSIC_PLAYING_DATA = \$_[0];
1833
1834 my $rwops = $meta->{path}
1835 ? new_from_file CFPlus::RW $meta->{path}
1836 : new CFPlus::RW $$MUSIC_PLAYING_DATA;
1837
1838 $MUSIC_PLAYER = new CFPlus::MixMusic $rwops
1839 or ((warn CFPlus::Mix_GetError), return); # pretty fatal error
1840
1841 my $NOW = time;
1842
1843 if ($MUSIC_PLAYING_META->{stop_time} > $NOW - $MUSIC_RESUME) {
1844 my $pos = $MUSIC_PLAYING_META->{stop_pos};
1845 $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
1846 $MUSIC_START = time - $pos;
1847 } else {
1848 $MUSIC_PLAYER->play (0);
1849 $MUSIC_START = time;
1850 }
1851
1852 delete $MUSIC_PLAYING_META->{stop_time};
1853 delete $MUSIC_PLAYING_META->{stop_pos};
1854 }
1855}
1856
1857sub audio_music_changed {
1858 return unless $CFG->{bgm_enable};
1859 return unless $SDL_MIXER;
1860
1861 # default MUSIC_WANT == MUSIC_DEFAULT
1862 @MUSIC_WANT = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_WANT;
1863
1864 # if the currently playing song is acceptable, let it continue
1865 return if $MUSIC_PLAYING_META
1866 && grep $MUSIC_PLAYING_META == $_, @MUSIC_WANT;
1867
1868 my $NOW = time;
1869
1870 if ($MUSIC_PLAYING_META) {
1871 $MUSIC_PLAYING_META->{stop_time} = $NOW;
1872 $MUSIC_PLAYING_META->{stop_pos} = $NOW - $MUSIC_START;
1873 CFPlus::MixMusic::fade_out 1000;
1874 } else {
1875 # sort by stop time, oldest first
1876 @MUSIC_WANT = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_WANT;
1877
1878 # if the most recently-played piece played very recently,
1879 # resume it, else choose the oldest piece for rotation.
1880 $MUSIC_PLAYING_META =
1881 $MUSIC_WANT[-1]{stop_time} > $NOW - $MUSIC_RESUME
1882 ? $MUSIC_WANT[-1]
1883 : $MUSIC_WANT[0];
1884
1885 audio_music_start;
1886 }
1887}
1888
1889sub audio_music_finished {
1890 undef $MUSIC_PLAYER;
1891 undef $MUSIC_PLAYING_META;
1892 undef $MUSIC_PLAYING_DATA;
1893
1894 audio_music_changed;
1895}
1896
1897sub audio_init {
1898 if ($CFG->{audio_enable}) {
1899 if (open my $fh, "<", CFPlus::find_rcfile "sounds/config") {
1900 $ENV{MIX_EFFECTSMAXSPEED} = 1;
1901 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
1902
1903 unless ($SDL_MIXER) {
1904 status "Unable to open sound device: there will be no sound";
1905 return;
1906 }
1907
1908 CFPlus::Mix_AllocateChannels 8;
1909 CFPlus::MixMusic::volume $CFG->{bgm_volume} * 128;
1910
1911 audio_music_finished;
1912
1913 local $_;
1914 while (<$fh>) {
1915 next if /^\s*#/;
1916 next if /^\s*$/;
1917
1918 my ($file, $volume, $event) = split /\s+/, $_, 3;
1919
1920 push @SOUNDS, "$volume,$file";
1921
1922 $AUDIO_CHUNKS{"$volume,$file"} ||= do {
1923 my $rwops = new_from_file CFPlus::RW CFPlus::find_rcfile "sounds/$file";
1924 my $chunk = new CFPlus::MixChunk $rwops;
1925 $chunk->volume ($volume * 128 / 100);
1926 $chunk
1927 };
1928 }
1929 } else {
1930 status "unable to open sound config: $!";
1931 }
1932 } else {
1933 undef $SDL_MIXER;
1934 }
1935}
1936
1937sub audio_shutdown {
1938 CFPlus::Mix_CloseAudio if $SDL_MIXER;
1939 undef $SDL_MIXER;
1940 @SOUNDS = ();
1941 %AUDIO_CHUNKS = ();
1942}
1943
1944my %animate_object; 2090my %animate_object;
1945my $animate_timer; 2091my $animate_timer;
1946 2092
1947my $fps = 9; 2093my $fps = 9;
1948 2094
1949my %demo;#d#
1950
1951sub force_refresh { 2095sub force_refresh {
2096 if ($ENV{CFPLUS_DEBUG} & 4) {
1952 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05; 2097 $fps = $fps * 0.98 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.02;
1953 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4; 2098 debug sprintf "%3.2f", $fps;
2099 }
1954 2100
1955 $CFPlus::UI::ROOT->draw; 2101 $CFPlus::UI::ROOT->draw;
1956 2102 CFPlus::SDL_GL_SwapBuffers;
1957 $WANT_REFRESH = 0;
1958 $CAN_REFRESH = 0;
1959 $LAST_REFRESH = $NOW; 2103 $LAST_REFRESH = $NOW;
1960 2104 $WANT_REFRESH->stop;
1961 CFPlus::SDL_GL_SwapBuffers;
1962} 2105}
1963 2106
2107$WANT_REFRESH = Event->idle (min => 0.001, max => 0.06, parked => 1, cb => \&force_refresh);
2108
1964my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub { 2109my $input = Event->timer (after => 0, hard => 0, interval => 1 / 50, cb => sub {
1965 $NOW = time; 2110 $NOW = time;
1966 2111
1967 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_) 2112 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
1968 for CFPlus::poll_events; 2113 for CFPlus::poll_events;
1969 2114
1970 if (%animate_object) { 2115 if (%animate_object) {
1971 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object; 2116 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
1972 ++$WANT_REFRESH; 2117 $WANT_REFRESH->start;
1973 }
1974
1975 if ($WANT_REFRESH) {
1976 force_refresh;
1977 } else {
1978 $CAN_REFRESH = 1;
1979 } 2118 }
1980}); 2119});
1981 2120
1982sub animation_start { 2121sub animation_start {
1983 my ($widget) = @_; 2122 my ($widget) = @_;
2039 2178
2040 CFPlus::UI::set_layout ($::CFG->{layout}); 2179 CFPlus::UI::set_layout ($::CFG->{layout});
2041 2180
2042 my %DEF_CFG = ( 2181 my %DEF_CFG = (
2043 sdl_mode => 0, 2182 sdl_mode => 0,
2044 width => 640,
2045 height => 480,
2046 fullscreen => 0, 2183 fullscreen => 0,
2047 fast => 0, 2184 fast => 0,
2048 map_scale => 1, 2185 map_scale => 1,
2049 fow_enable => 1, 2186 fow_enable => 1,
2050 fow_intensity => 0, 2187 fow_intensity => 0,
2054 gauge_fontsize => 1, 2191 gauge_fontsize => 1,
2055 gauge_size => 0.35, 2192 gauge_size => 0.35,
2056 stat_fontsize => 0.7, 2193 stat_fontsize => 0.7,
2057 mapsize => 100, 2194 mapsize => 100,
2058 audio_enable => 1, 2195 audio_enable => 1,
2196 effects_enable => 1,
2197 effects_volume => 1,
2059 bgm_enable => 1, 2198 bgm_enable => 1,
2060 bgm_volume => 0.25, 2199 bgm_volume => 0.5,
2061 output_sync => 1, 2200 output_sync => 1,
2062 output_count => 1, 2201 output_count => 1,
2063 output_rate => "", 2202 output_rate => "",
2064 pickup => 0, 2203 pickup => 0,
2065 inv_sort => "mtime", 2204 inv_sort => "mtime",
2143Event::loop; 2282Event::loop;
2144#CFPlus::SDL_Quit; 2283#CFPlus::SDL_Quit;
2145#CFPlus::_exit 0; 2284#CFPlus::_exit 0;
2146 2285
2147END { 2286END {
2287 video_shutdown;
2288 audio_shutdown;
2148 CFPlus::SDL_Quit; 2289 CFPlus::SDL_Quit;
2149 CFPlus::DB::Server::stop; 2290 CFPlus::DB::Server::stop;
2150} 2291}
2151 2292
2152=head1 NAME 2293=head1 NAME
2153 2294
2154cfplus - A Crossfire+ and Crossfire game client 2295cfplus - A Crossfire TRT and Crossfire game client
2155 2296
2156=head1 SYNOPSIS 2297=head1 SYNOPSIS
2157 2298
2158Just run it - no commandline arguments are supported. 2299Just run it - no commandline arguments are supported.
2159 2300
2160=head1 USAGE 2301=head1 USAGE
2161 2302
2162cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used 2303cfplus utilises OpenGL for all UI elements and the game. It is supposed to
2163fullscreen and interactively. 2304be used in fullscreen mode and interactively.
2164 2305
2165=head1 DEBUGGING 2306=head1 DEBUGGING
2166 2307
2167 2308
2168CFPLUS_DEBUG - environment variable 2309CFPLUS_DEBUG - environment variable

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines