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.178 by root, Fri Jul 20 16:32:04 2007 UTC vs.
Revision 1.226 by root, Sat Sep 1 07:22:21 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;
82use CFPlus; 83use CFPlus;
83use CFPlus::OpenGL (); 84use CFPlus::OpenGL ();
84use CFPlus::Protocol; 85use CFPlus::Protocol;
85use CFPlus::DB; 86use CFPlus::DB;
86use CFPlus::UI; 87use CFPlus::UI;
88use CFPlus::UI::Canvas;
87use CFPlus::UI::Inventory; 89use CFPlus::UI::Inventory;
88use CFPlus::UI::SpellList; 90use CFPlus::UI::SpellList;
91use CFPlus::UI::Dockable;
92use CFPlus::UI::MessageWindow;
93use CFPlus::UI::ChatView;
89use CFPlus::Pod; 94use CFPlus::Pod;
90use CFPlus::MapWidget; 95use CFPlus::MapWidget;
91use CFPlus::Macro; 96use CFPlus::Macro;
92 97
93$SIG{QUIT} = sub { Carp::cluck "QUIT" }; 98$SIG{QUIT} = sub { Carp::cluck "QUIT" };
110our $CONN; 115our $CONN;
111our $PROFILE; # current profile 116our $PROFILE; # current profile
112our $FAST; # fast, low-quality mode, possibly useful for software-rendering 117our $FAST; # fast, low-quality mode, possibly useful for software-rendering
113 118
114our $WANT_REFRESH; 119our $WANT_REFRESH;
115our $CAN_REFRESH;
116 120
117our @SDL_MODES; 121our @SDL_MODES;
118our $WIDTH; 122our $WIDTH;
119our $HEIGHT; 123our $HEIGHT;
120our $FULLSCREEN; 124our $FULLSCREEN;
124our $FONT_FIXED; 128our $FONT_FIXED;
125 129
126our $MAP; 130our $MAP;
127our $MAPMAP; 131our $MAPMAP;
128our $MAPWIDGET; 132our $MAPWIDGET;
133our $COMPLETER;
129our $BUTTONBAR; 134our $BUTTONBAR;
130our $LOGVIEW;
131our $CONSOLE;
132our $METASERVER; 135our $METASERVER;
133our $LOGIN_BUTTON; 136our $LOGIN_BUTTON;
134our $QUIT_DIALOG; 137our $QUIT_DIALOG;
135our $HOST_ENTRY; 138our $HOST_ENTRY;
136our $FULLSCREEN_ENABLE; 139our $FULLSCREEN_ENABLE;
143our $SETUP_KEYBOARD; 146our $SETUP_KEYBOARD;
144 147
145our $PL_NOTEBOOK; 148our $PL_NOTEBOOK;
146our $PL_WINDOW; 149our $PL_WINDOW;
147 150
151our $MUSIC_PLAYING_WIDGET;
152our $LICENSE_WIDGET;
153
154our $PICKUP_PAGE;
148our $INVENTORY_PAGE; 155our $INVENTORY_PAGE;
149our $STATS_PAGE; 156our $STATS_PAGE;
150our $SKILL_PAGE; 157our $SKILL_PAGE;
151our $SPELL_PAGE; 158our $SPELL_PAGE;
152our $SPELL_LIST; 159our $SPELL_LIST;
158our $STATWIDS; 165our $STATWIDS;
159 166
160our $SDL_ACTIVE; 167our $SDL_ACTIVE;
161our %SDL_CB; 168our %SDL_CB;
162 169
163our $SDL_MIXER;
164our $MUSIC_DEFAULT = "in_a_heartbeat.ogg";
165our @MUSIC_WANT;
166our $MUSIC_START;
167our $MUSIC_PLAYING;
168our $MUSIC_PLAYER;
169our $MUSIC_RESUME = 30; # resume music when players less than these many seconds before
170our @SOUNDS; # event => file mapping
171our %AUDIO_CHUNKS; # audio files
172
173our $ALT_ENTER_MESSAGE; 170our $ALT_ENTER_MESSAGE;
174our $STATUSBOX; 171our $STATUSBOX;
175our $DEBUG_STATUS; 172our $DEBUG_STATUS;
176 173
177our $INV; 174our $INV;
178our $INVR; 175our $INVR;
179our $INV_RIGHT_HB; 176our $INVR_HB;
180 177
181our $PICKUP_CFG; 178#############################################################################
182
183our $IN_BUILD_MODE;
184our $BUILD_BUTTON;
185 179
186sub status { 180sub status {
187 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]); 181 $STATUSBOX->add (CFPlus::asxml $_[0], pri => -10, group => "status", timeout => 10, fg => [1, 1, 0, 1]);
188} 182}
189 183
190sub debug { 184sub debug {
191 $DEBUG_STATUS->set_text ($_[0]); 185 $DEBUG_STATUS->set_text ($_[0]);
192} 186}
193 187
194sub message { 188sub message {
189 $MESSAGE_WINDOW->message (@_);
190}
191
192#############################################################################
193#TODO: maybe move into own audio module...
194
195our $SDL_MIXER;
196
197our $MUSIC_DEFAULT = "in_a_heartbeat.ogg";
198our $MUSIC_WANT; # arryref of ambient music we want to play
199our @MUSIC_HAVE; # ambient music we have on disk
200our $MUSIC_START;
201our @MUSIC_JINGLE; # which jingles to play next
202our $MUSIC_PLAYING_DATA;
203our $MUSIC_PLAYING_META;
204our $MUSIC_PLAYER;
205our $MUSIC_RESUME = 30; # resume music when played less than these many seconds before
206
207our %AUDIO_CHUNK; # audio "files"
208our %AUDIO_PLAY; # which audio faces should be played
209
210sub audio_channel_finished {
211 my ($channel) = @_;
212
213# warn "channel $channel finished\n";#d#
214}
215
216sub audio_sound_push($) {
195 my ($para) = @_; 217 my ($face) = @_;
196 218
197 my $time = sprintf "%02d:%02d:%02d", (localtime time)[2,1,0]; 219 $CFG->{effects_enable}
220 or return;
198 221
199 $para->{markup} = "<span foreground='#ffffff'>$time</span> $para->{markup}"; 222 $AUDIO_PLAY{$face}
223 or return;
200 224
201 $LOGVIEW->add_paragraph ($para); 225 if (my $chunk = $AUDIO_CHUNK{$face}) {
202 $LOGVIEW->scroll_to_bottom; 226 for (grep $_->[0] >= Event::time, @{(delete $AUDIO_PLAY{$face}) || []}) {
227 my (undef, $dx, $dy, $vol) = @$_;
228
229 my $channel = CFPlus::Channel::find;
230 $channel->volume ($vol * $CFG->{effects_volume} * 128 / 255);
231 $channel->set_position_r ($dx, $dy, 20);
232 $chunk->play ($channel);
233 }
234 } else {
235 # sound_meta not set means data is in flight either way
236 my $meta = $CONN->{face}[$face]
237 or return;
238
239 $meta->{data}
240 or return;
241
242 # if its a jingle, play it as ambient music
243 if ($meta->{data}{jingle}) {
244 if (delete $AUDIO_PLAY{$face}) { # take the jingle out of the sound queue
245 push @MUSIC_JINGLE, $meta; # push it oto the music/jingle queue
246 &audio_music_push ($face);
247 }
248 } else {
249 # fetch from database
250 CFPlus::DB::get res_data => $meta->{name}, sub {
251 my $rwops = new CFPlus::RW $_[0];
252 my $chunk = new CFPlus::MixChunk $rwops
253 or Carp::confess "sound face " . (JSON::XS::to_json $meta) . " unloadable: " . CFPlus::Mix_GetError;
254 $chunk->volume (($meta->{data}{volume} || 1) * 128);
255 $AUDIO_CHUNK{$face} = $chunk;
256
257 audio_sound_push ($face);
258 };
259 }
260 }
203} 261}
262
263sub audio_sound_play {
264 my ($face, $dx, $dy, $vol) = @_;
265
266 $SDL_MIXER
267 or return;
268 $CFG->{effects_enable}
269 or return;
270
271 my $queue = $AUDIO_PLAY{$face} ||= [];
272 push @$queue, [Event::time + 0.6, $dx, $dy, $vol]; # do not play sound for outdated events
273 audio_sound_push $face
274 unless @$queue > 1;
275}
276
277sub audio_music_set_meta {
278 my ($meta) = @_;
279
280 $MUSIC_PLAYING_META = $meta;
281 $MUSIC_PLAYING_WIDGET->set_markup (
282 "<b>Name</b>: " . (CFPlus::asxml $meta->{data}{name}) . "\n"
283 . "<b>Author</b>: " . (CFPlus::asxml $meta->{data}{author}) . "\n"
284 . "<b>Source</b>: " . (CFPlus::asxml $meta->{data}{source}) . "\n"
285 . "<b>License</b>: " . (CFPlus::asxml $meta->{data}{license})
286 );
287}
288
289sub audio_music_update_volume {
290 return unless $MUSIC_PLAYING_META;
291 my $volume = $MUSIC_PLAYING_META->{data}{volume} || 1;
292 my $base = $MUSIC_PLAYING_META->{data}{jingle} ? 1 : $CFG->{bgm_volume};
293 CFPlus::MixMusic::volume $base * $volume * 128;
294}
295
296sub audio_music_start {
297 my $meta = $MUSIC_PLAYING_META;
298
299 CFPlus::DB::get res_data => $meta->{name}, sub {
300 return unless $SDL_MIXER;
301
302 # music might have changed...
303 $meta eq $MUSIC_PLAYING_META
304 or return &audio_music_start ();
305
306 audio_music_update_volume;
307
308 $MUSIC_PLAYING_DATA = \$_[0];
309
310 my $rwops = $meta->{path}
311 ? new_from_file CFPlus::RW $meta->{path}
312 : new CFPlus::RW $$MUSIC_PLAYING_DATA;
313
314 $MUSIC_PLAYER = new CFPlus::MixMusic $rwops
315 or Carp::confess "music face $meta->{face} unloadable: " . CFPlus::Mix_GetError;
316
317 my $NOW = time;
318
319 if ($MUSIC_PLAYING_META->{stop_time} > $NOW - $MUSIC_RESUME) {
320 my $pos = $MUSIC_PLAYING_META->{stop_pos};
321 $MUSIC_PLAYER->fade_in_pos (0, 700, $pos);
322 $MUSIC_START = time - $pos;
323 } else {
324 $MUSIC_PLAYER->play (0);
325 $MUSIC_START = time;
326 }
327
328 delete $meta->{stop_time};
329 delete $meta->{stop_pos};
330 }
331}
332
333sub audio_music_push {
334 return unless $SDL_MIXER;
335
336 my $fade_out;
337
338 if (@MUSIC_JINGLE) {
339 @MUSIC_HAVE = $MUSIC_JINGLE[0];
340 $fade_out = 333;
341 } else {
342 return unless $CFG->{bgm_enable};
343
344 my @have =
345 grep $_ && $_->{data},
346 map $CONN->{face}[$_],
347 @$MUSIC_WANT;
348
349 # randomize music a bit so that the order is not always the same
350 $_->{stop_time} ||= rand for @have;
351
352 @MUSIC_HAVE = @have
353 if @have;
354
355 # default MUSIC_HAVE == MUSIC_DEFAULT
356 @MUSIC_HAVE = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_HAVE;
357 $fade_out = 700;
358 }
359
360 # if the currently playing song is acceptable, let it continue
361 return if grep $MUSIC_PLAYING_META == $_, @MUSIC_HAVE;
362
363 my $NOW = time;
364
365 if ($MUSIC_PLAYING_META) {
366 $MUSIC_PLAYING_META->{stop_time} = $NOW;
367 $MUSIC_PLAYING_META->{stop_pos} = $NOW - $MUSIC_START;
368 CFPlus::MixMusic::fade_out $fade_out;
369 } else {
370 # sort by stop time, oldest first
371 @MUSIC_HAVE = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_HAVE;
372
373 # if the most recently-played piece played very recently,
374 # resume it, else choose the oldest piece for rotation.
375 audio_music_set_meta
376 $MUSIC_HAVE[-1]{stop_pos} && $MUSIC_HAVE[-1]{stop_time} > $NOW - $MUSIC_RESUME
377 ? $MUSIC_HAVE[-1]
378 : $MUSIC_HAVE[0];
379
380 audio_music_start;
381 }
382}
383
384sub audio_music_set_ambient {
385 my ($songs) = @_;
386
387 $MUSIC_WANT = $songs;
388 audio_music_push;
389}
390
391sub audio_music_finished {
392 if ($MUSIC_PLAYING_META) {
393 $MUSIC_PLAYING_META->{stop_time} = time;
394 }
395
396 # we compress multiple jingles of the same type
397 shift @MUSIC_JINGLE
398 while @MUSIC_JINGLE && $MUSIC_PLAYING_META == $MUSIC_JINGLE[0];
399
400 $MUSIC_PLAYING_WIDGET->clear;
401
402 undef $MUSIC_PLAYER;
403 undef $MUSIC_PLAYING_META;
404 undef $MUSIC_PLAYING_DATA;
405
406 audio_music_push;
407}
408
409sub audio_init {
410 if ($CFG->{audio_enable}) {
411 $ENV{MIX_EFFECTSMAXSPEED} = 1;
412 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
413
414 unless ($SDL_MIXER) {
415 status "Unable to open sound device: there will be no sound";
416 return;
417 }
418
419 CFPlus::Mix_AllocateChannels 16;
420
421 audio_music_finished;
422 } else {
423 undef $SDL_MIXER;
424 }
425}
426
427sub audio_shutdown {
428 undef $MUSIC_PLAYER;
429 undef $MUSIC_PLAYING_META;
430 undef $MUSIC_PLAYING_DATA;
431
432 $MUSIC_WANT = [];
433 @MUSIC_JINGLE = ();
434 %AUDIO_PLAY = ();
435 %AUDIO_CHUNK = ();
436
437 CFPlus::Mix_CloseAudio if $SDL_MIXER;
438 undef $SDL_MIXER;
439}
440
441#############################################################################
204 442
205sub destroy_query_dialog { 443sub destroy_query_dialog {
206 (delete $_[0]{query_dialog})->destroy 444 (delete $_[0]{query_dialog})->destroy
207 if $_[0]{query_dialog}; 445 if $_[0]{query_dialog};
208} 446}
460 maph => $mapsize, 698 maph => $mapsize,
461 699
462 client => "cfplus $CFPlus::VERSION $] $^O", 700 client => "cfplus $CFPlus::VERSION $] $^O",
463 701
464 map_widget => $MAPWIDGET, 702 map_widget => $MAPWIDGET,
465 logview => $LOGVIEW,
466 statusbox => $STATUSBOX, 703 statusbox => $STATUSBOX,
467 map => $MAP, 704 map => $MAP,
468 mapmap => $MAPMAP, 705 mapmap => $MAPMAP,
469 query => \&server_query, 706 query => \&server_query,
470 707
471 setup_req => { 708 setup_req => {
472 smoothing => $CFG->{map_smoothing}*1, 709 smoothing => $CFG->{map_smoothing}*1,
473 },
474
475 sound_play => sub {
476 my ($x, $y, $soundnum, $type) = @_;
477
478 $SDL_MIXER
479 or return;
480
481 my $chunk = $AUDIO_CHUNKS{$SOUNDS[$soundnum]}
482 or return;
483
484 $chunk->play;
485 }, 710 },
486 }; 711 };
487 712
488 if ($CONN) { 713 if ($CONN) {
489 CFPlus::lowdelay fileno $CONN->{fh}; 714 CFPlus::lowdelay fileno $CONN->{fh};
501 $SETUP_DIALOG->show; 726 $SETUP_DIALOG->show;
502 $PL_WINDOW->hide; 727 $PL_WINDOW->hide;
503 $SPELL_LIST->clear_spells; 728 $SPELL_LIST->clear_spells;
504 $CFPlus::UI::ROOT->emit (stop_game => ! ! $CONN); 729 $CFPlus::UI::ROOT->emit (stop_game => ! ! $CONN);
505 730
506 &audio_music_set ([]); 731 &audio_music_set_ambient ([]);
507 732
508 return unless $CONN; 733 return unless $CONN;
509 734
510 status "connection closed"; 735 status "connection closed";
511 736
553 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fullscreen"); 778 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fullscreen");
554 $table->add_at (1, $row++, $FULLSCREEN_ENABLE = new CFPlus::UI::CheckBox 779 $table->add_at (1, $row++, $FULLSCREEN_ENABLE = new CFPlus::UI::CheckBox
555 state => $CFG->{fullscreen}, 780 state => $CFG->{fullscreen},
556 tooltip => "Bring the client into fullscreen mode.", 781 tooltip => "Bring the client into fullscreen mode.",
557 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 } 782 on_changed => sub { my ($self, $value) = @_; $CFG->{fullscreen} = $value; 0 }
783 );
784
785 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Force OpenGL 1.1");
786 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
787 state => $CFG->{force_opengl11},
788 tooltip => "Limit CFPlus to use OpenGL 1.1 features only. This will normally result in "
789 . "higher memory usage and slower performance. It will, however, help tremendously on "
790 . "cards that claim to support a feature but fall back to software rendering. "
791 . "Nvidia Geforce FX cards are known to claim features the hardware doesn't support, "
792 . "but cards and drivers from other vendors (ATI) are often just as bad. <b>If you "
793 . "experience extremely low framerates and your card should do better, try this option.</b>",
794 on_changed => sub { my ($self, $value) = @_; $CFG->{force_opengl11} = $value; 0 }
795 );
796
797 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Compress Textures");
798 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
799 state => $CFG->{texture_compression},
800 tooltip => "Use texture compression. Normally this will not reduce visual quality noticable but "
801 . "will save a lot of memory and increase performance. The compression algorithm "
802 . "can differ form card to card, so your mileage may vary. This setting is ignored in "
803 . "forced OpenGL 1.1 mode.",
804 on_changed => sub { my ($self, $value) = @_; $CFG->{texture_compression} = $value; 0 }
558 ); 805 );
559 806
560 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fast & Ugly"); 807 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fast & Ugly");
561 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 808 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
562 state => $CFG->{fast}, 809 state => $CFG->{fast},
590 837
591 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing"); 838 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Smoothing");
592 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 839 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
593 state => $CFG->{map_smoothing}, 840 state => $CFG->{map_smoothing},
594 tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. " 841 tooltip => "<b>Map Smoothing</b> tries to make tile borders less square. "
595 . "This increases load on the graphics subsystem and works only with 2.x servers. " 842 . "This increases load on the graphics subsystem and works only with TRT servers. "
596 . "Changes take effect at next connection only.", 843 . "Changes take effect at next login only.",
597 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 } 844 on_changed => sub { my ($self, $value) = @_; $CFG->{map_smoothing} = $value; 0 }
598 ); 845 );
599 846
600 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War"); 847 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Fog of War");
601 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 848 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
612 ); 859 );
613 860
614 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Message Fontsize"); 861 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Message Fontsize");
615 $table->add_at (1, $row++, new CFPlus::UI::Slider 862 $table->add_at (1, $row++, new CFPlus::UI::Slider
616 range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1], 863 range => [$CFG->{log_fontsize}, 0.5, 2, 0, 0.1],
617 tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant.", 864 tooltip => "The font size used by the <b>message/server log</b> window only. Changes are instant, "
865 . "but you still need to press apply to correctly re-layout the widget.",
618 on_changed => sub { $LOGVIEW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 }, 866 on_changed => sub { $MESSAGE_WINDOW->set_fontsize ($CFG->{log_fontsize} = $_[1]); 0 },
619 ); 867 );
620 868
621 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge fontsize"); 869 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Gauge fontsize");
622 $table->add_at (1, $row++, new CFPlus::UI::Slider 870 $table->add_at (1, $row++, new CFPlus::UI::Slider
623 range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1], 871 range => [$CFG->{gauge_fontsize}, 0.5, 2, 0, 0.1],
644} 892}
645 893
646sub audio_setup { 894sub audio_setup {
647 my $vbox = new CFPlus::UI::VBox; 895 my $vbox = new CFPlus::UI::VBox;
648 896
649 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]); 897 $vbox->add (my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 0, 1]);
650 898
651 my $row = 0; 899 my $row = 0;
652 900
653 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable"); 901 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Audio Enable");
654 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 902 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
658 ); 906 );
659# $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume"); 907# $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Effects Volume");
660# $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub { 908# $table->add_at (1, 8, new CFPlus::UI::Slider range => [$CFG->{effects_volume}, 0, 128, 1], on_changed => sub {
661# $CFG->{effects_volume} = $_[1]; 909# $CFG->{effects_volume} = $_[1];
662# }); 910# });
911
912 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Sound Effects");
913 $table->add_at (1, $row, new CFPlus::UI::CheckBox
914 expand => 1, state => $CFG->{effects_enable},
915 tooltip => "If enabled, sound effects are enabled. If disabled, no sound effects will be played.",
916 on_changed => sub {
917 $CFG->{effects_enable} = $_[1];
918 $CONN->update_fx_want if $CONN;
919 0
920 }
921 );
922 $table->add_at (2, $row++, new CFPlus::UI::Slider
923 expand => 1, range => [$CFG->{effects_volume}, 0, 1, 0, 1/128],
924 tooltip => "The relative volume of sound effects. Best audio quality is achieved if this "
925 . "is set highest (rightmost) and you use your operating system volume setting. Changes are instant.",
926 on_changed => sub { $CFG->{effects_volume} = $_[1]; 0 }
927 );
928
663 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music"); 929 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Background Music");
664 $table->add_at (1, $row++, my $hbox = new CFPlus::UI::HBox); 930 $table->add_at (1, $row, new CFPlus::UI::CheckBox
665 $hbox->add (new CFPlus::UI::CheckBox
666 expand => 1, state => $CFG->{bgm_enable}, 931 expand => 1, state => $CFG->{bgm_enable},
667 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.", 932 tooltip => "If enabled, playing of background music is enabled. If disabled, no background music will be played.",
668 on_changed => sub { $CFG->{bgm_enable} = $_[1]; 0 } 933 on_changed => sub {
934 $CFG->{bgm_enable} = $_[1];
935 $CONN->update_fx_want if $CONN;
936 audio_music_push;
937 0
938 }
669 ); 939 );
670 $hbox->add (new CFPlus::UI::Slider 940 $table->add_at (2, $row++, new CFPlus::UI::Slider
671 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128], 941 expand => 1, range => [$CFG->{bgm_volume}, 0, 1, 0, 1/128],
672 tooltip => "The volume of the background music. Changes are instant.", 942 tooltip => "The volume of the background music. Changes are instant.",
673 on_changed => sub { $CFG->{bgm_volume} = $_[1]; CFPlus::MixMusic::volume $_[1] * 128; 0 } 943 on_changed => sub { $CFG->{bgm_volume} = $_[1]; audio_music_update_volume; 0 }
674 ); 944 );
675 945
676 $table->add_at (1, $row++, new CFPlus::UI::Button 946 $table->add_at (1, $row++, new CFPlus::UI::Button
677 expand => 1, align => 0, text => "Apply", 947 c_colspan => 2, expand => 1, align => 0, text => "Apply",
678 tooltip => "Apply the audio settings", 948 tooltip => "Apply the audio settings",
679 on_activate => sub { 949 on_activate => sub {
680 audio_shutdown (); 950 audio_shutdown ();
681 audio_init (); 951 audio_init ();
682 0 952 0
724 $hb->add (my $hg = new CFPlus::UI::Gauge type => 'hp', tooltip => "#stat_health"); 994 $hb->add (my $hg = new CFPlus::UI::Gauge type => 'hp', tooltip => "#stat_health");
725 $hb->add (my $mg = new CFPlus::UI::Gauge type => 'mana', tooltip => "#stat_mana"); 995 $hb->add (my $mg = new CFPlus::UI::Gauge type => 'mana', tooltip => "#stat_mana");
726 $hb->add (my $gg = new CFPlus::UI::Gauge type => 'grace', tooltip => "#stat_grace"); 996 $hb->add (my $gg = new CFPlus::UI::Gauge type => 'grace', tooltip => "#stat_grace");
727 $hb->add (my $fg = new CFPlus::UI::Gauge type => 'food', tooltip => "#stat_food"); 997 $hb->add (my $fg = new CFPlus::UI::Gauge type => 'food', tooltip => "#stat_food");
728 998
729 $vbox->add (my $exp = new CFPlus::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1, tooltip => "#stat_exp"); 999 $vbox->add (my $exp = new CFPlus::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1, tooltip => "#stat_exp");
1000 $vbox->add (my $prg = new CFPlus::UI::ExperienceProgress);
1001 $vbox->add (my $sklprg = new CFPlus::UI::ExperienceProgress);
730 $vbox->add (my $rng = new CFPlus::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1, tooltip => "#stat_ranged"); 1002 $vbox->add (my $rng = new CFPlus::UI::Label valign => 0, align => 1, can_hover => 1, can_events => 1, tooltip => "#stat_ranged");
731 1003
732 $GAUGES = { 1004 $GAUGES = {
1005 exp => $exp, prg => $prg, sklprg => $sklprg,
733 exp => $exp, win => $win, range => $rng, 1006 win => $win, range => $rng,
734 food => $fg, mana => $mg, hp => $hg, grace => $gg 1007 hp => $hg, mana => $mg, grace => $gg, food => $fg,
735 }; 1008 };
736 1009
737 &set_gauge_window_fontsize; 1010 &set_gauge_window_fontsize;
738 1011
739 $win 1012 $win
751 $table->add_at (0, 3, new CFPlus::UI::Label text => "Suppress Tooltips"); 1024 $table->add_at (0, 3, new CFPlus::UI::Label text => "Suppress Tooltips");
752 $table->add_at (1, 3, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 }); 1025 $table->add_at (1, 3, new CFPlus::UI::CheckBox on_changed => sub { $ENV{CFPLUS_DEBUG} ^= 8; 0 });
753 $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } ); 1026 $table->add_at (0, 4, new CFPlus::UI::Button text => "die on click(tm)", on_activate => sub { &CFPlus::debug() } );
754 1027
755 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d# 1028 $table->add_at (0, 5, new CFPlus::UI::TextEdit text => "line1\0152\0153");#d#
1029
1030 $table->add_at (7,7, my $t = new CFPlus::UI::Table expand => 0);
1031 $t->add_at (0,0, new CFPlus::UI::Label text => "a a a a", c_rowspan => 1, c_colspan => 2);
1032 $t->add_at (2,0, new CFPlus::UI::Label text => "b\nb", c_rowspan => 2, c_colspan => 1);
1033 $t->add_at (1,2, new CFPlus::UI::Label text => "c c c c", c_rowspan => 1, c_colspan => 2);
1034 $t->add_at (0,1, new CFPlus::UI::Label text => "d\nd", c_rowspan => 2, c_colspan => 1);
1035 $t->add_at (1,1, new CFPlus::UI::Label text => "e");
1036
1037 $table->add_at (7, 6, my $c = new CFPlus::UI::Canvas);
1038
1039 $c->add_items ({
1040 type => "line_loop",
1041 color => [0, 1, 0],
1042 width => 9,
1043 coord_mode => "abs",
1044 coord => [[10, 5], [5, 50], [20, 5], [5, 60]],
1045 });
1046
1047 $c->add_items ({
1048 type => "lines",
1049 color => [1, 1, 0],
1050 width => 2,
1051 coord_mode => "rel",
1052 coord => [[0,0], [1,1], [1,0], [0,1]],
1053 });
1054
1055 $c->add_items ({
1056 type => "polygon",
1057 color => [0, 0.43, 0],
1058 width => 2,
1059 coord_mode => "rel",
1060 coord => [[0,0.2], [1,.4], [1,.6], [0,.8]],
1061 });
756 1062
757 $table 1063 $table
758} 1064}
759 1065
760sub stats_window { 1066sub stats_window {
907 $r 1213 $r
908} 1214}
909 1215
910sub skill_window { 1216sub skill_window {
911 my $sw = new CFPlus::UI::ScrolledWindow (expand => 1); 1217 my $sw = new CFPlus::UI::ScrolledWindow (expand => 1);
1218
912 $sw->add ($STATWIDS->{skill_tbl} = new CFPlus::UI::Table expand => 1, col_expand => [0, 0, 1, 0, 0, 1]); 1219 $sw->add ($STATWIDS->{skill_tbl} = new CFPlus::UI::Table expand => 1, col_expand => [0, 0, 1, .1, 0, 0, 1, .1]);
1220
913 $sw 1221 $sw
914} 1222}
915 1223
916sub formsep($) { 1224sub formsep($) {
917 scalar reverse join ",", unpack "(A3)*", reverse $_[0] * 1 1225 scalar reverse join ",", unpack "(A3)*", reverse $_[0] * 1
1058 text => $CFG->{profile}{default}{host}, 1366 text => $CFG->{profile}{default}{host},
1059 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to", 1367 tooltip => "The hostname or ip address of the Crossfire(+) server to connect to",
1060 on_changed => sub { 1368 on_changed => sub {
1061 my ($self, $value) = @_; 1369 my ($self, $value) = @_;
1062 $CFG->{profile}{default}{host} = $value; 1370 $CFG->{profile}{default}{host} = $value;
1063 0 1371 1
1064 } 1372 }
1065 ); 1373 );
1066 1374
1067 $vbox->add (new CFPlus::UI::Button 1375 $vbox->add (new CFPlus::UI::Button
1068 expand => 1, 1376 expand => 1,
1069 text => "Server List", 1377 text => "Server List",
1070 other => $METASERVER, 1378 other => $METASERVER,
1071 tooltip => "Show a list of available crossfire servers", 1379 tooltip => "Show a list of available crossfire servers",
1072 on_activate => sub { $METASERVER->toggle_visibility; 0 }, 1380 on_activate => sub { $METASERVER->toggle_visibility; 0 },
1073 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 0 }, 1381 on_visibility_change => sub { $METASERVER->hide unless $_[1]; 1 },
1074 ); 1382 );
1075 } 1383 }
1076 1384
1077 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username"); 1385 $table->add_at (0, 4, new CFPlus::UI::Label valign => 0, align => 1, text => "Username");
1078 $table->add_at (1, 4, new CFPlus::UI::Entry 1386 $table->add_at (1, 4, new CFPlus::UI::Entry
1079 text => $CFG->{profile}{default}{user}, 1387 text => $CFG->{profile}{default}{user},
1080 tooltip => "The name of your character on the server", 1388 tooltip => "The name of your character on the server",
1081 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value } 1389 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value; 1 }
1082 ); 1390 );
1083 1391
1084 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password"); 1392 $table->add_at (0, 5, new CFPlus::UI::Label valign => 0, align => 1, text => "Password");
1085 $table->add_at (1, 5, new CFPlus::UI::Entry 1393 $table->add_at (1, 5, new CFPlus::UI::Entry
1086 text => $CFG->{profile}{default}{password}, 1394 text => $CFG->{profile}{default}{password},
1087 hidden => 1, 1395 hidden => 1,
1088 tooltip => "The password for your character", 1396 tooltip => "The password for your character",
1089 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value } 1397 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value; 1 }
1090 ); 1398 );
1091 1399
1092 $table->add_at (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size"); 1400 $table->add_at (0, 7, new CFPlus::UI::Label valign => 0, align => 1, text => "Map Size");
1093 $table->add_at (1, 7, new CFPlus::UI::Slider 1401 $table->add_at (1, 7, new CFPlus::UI::Slider
1094 force_w => 100, 1402 force_w => 100,
1095 range => [$CFG->{mapsize}, 10, 100, 0, 1], 1403 range => [$CFG->{mapsize}, 10, 100, 0, 1],
1096 tooltip => "This is the size of the portion of the map update the server sends you. " 1404 tooltip => "This is the size of the portion of the map update the server sends you. "
1097 . "If you set this to a high value you will be able to see further, " 1405 . "If you set this to a high value you will be able to see further, "
1098 . "but you also increase bandwidth requirements and latency. " 1406 . "but you also increase bandwidth requirements and latency. "
1099 . "This option is only used once at log-in.", 1407 . "This option is only used once at log-in.",
1100 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 0 }, 1408 on_changed => sub { my ($self, $value) = @_; $CFG->{mapsize} = $self->{range}[0] = $value = int $value; 1 },
1101 ); 1409 );
1102 1410
1103 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Face Prefetch");
1104 $table->add_at (1, 8, new CFPlus::UI::CheckBox
1105 state => $CFG->{face_prefetch},
1106 tooltip => "<b>Background Image Prefetch</b>\n\n"
1107 . "If enabled, the client automatically pre-fetches images from the server. "
1108 . "This might increase or create lag, but increases the chances "
1109 . "of faces being ready for display when you encounter them. "
1110 . "It also uses up server bandwidth on every connect, "
1111 . "so only set it if you really need to prefetch images. "
1112 . "This option can be set and unset any time.",
1113 on_changed => sub { $CFG->{face_prefetch} = $_[1]; 0 },
1114 );
1115
1116 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate"); 1411 $table->add_at (0, 8, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Rate");
1412 $table->add_at (1, 8, new CFPlus::UI::Entry
1413 text => $CFG->{output_rate},
1414 tooltip => "The maximum bandwidth in bytes per second that the server should not exceed "
1415 . "when sending data. When 0 or unset, the server "
1416 . "default will be used, which is usually around 100kb/s. Most servers will "
1417 . "dynamically find an optimal rate, so adjust this only when necessary.",
1418 on_changed => sub { $CFG->{output_rate} = $_[1]; 1 },
1419 );
1420
1421 $table->add_at (0, 9, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1117 $table->add_at (1, 9, new CFPlus::UI::Entry 1422 $table->add_at (1, 9, new CFPlus::UI::Entry
1118 text => $CFG->{output_rate},
1119 tooltip => "The approximate bandwidth in bytes per second that the server should not exceed "
1120 . "when sending images, to ensure interactiveness. When 0 or unset, the server "
1121 . "default will be used, which is usually around 100kb/s.",
1122 on_changed => sub { $CFG->{output_rate} = $_[1]; 0 },
1123 );
1124
1125 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Count");
1126 $table->add_at (1, 10, new CFPlus::UI::Entry
1127 text => $CFG->{output_count}, 1423 text => $CFG->{output_count},
1128 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.", 1424 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1129 on_changed => sub { $CFG->{output_count} = $_[1]; 0 }, 1425 on_changed => sub { $CFG->{output_count} = $_[1]; 1 },
1130 ); 1426 );
1131 1427
1132 $table->add_at (0, 11, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync"); 1428 $table->add_at (0, 10, new CFPlus::UI::Label valign => 0, align => 1, text => "Output-Sync");
1133 $table->add_at (1, 11, new CFPlus::UI::Entry 1429 $table->add_at (1, 10, new CFPlus::UI::Entry
1134 text => $CFG->{output_sync}, 1430 text => $CFG->{output_sync},
1135 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.", 1431 tooltip => "Should be set to 1 unless you know what you are doing. This option is only used once at log-in.",
1136 on_changed => sub { $CFG->{output_sync} = $_[1]; 0 }, 1432 on_changed => sub { $CFG->{output_sync} = $_[1]; 1 },
1137 ); 1433 );
1138 1434
1139 $table->add_at (1, 12, $LOGIN_BUTTON = new CFPlus::UI::Button 1435 $table->add_at (1, 11, $LOGIN_BUTTON = new CFPlus::UI::Button
1140 expand => 1, 1436 expand => 1,
1141 align => 0, 1437 align => 0,
1142 text => "Login", 1438 text => "Login",
1143 on_activate => sub { 1439 on_activate => sub {
1144 $CONN ? stop_game 1440 $CONN ? stop_game
1145 : start_game; 1441 : start_game;
1146 0 1442 1
1147 }, 1443 },
1148 ); 1444 );
1149 1445
1150 $vbox->add (new CFPlus::UI::FancyFrame 1446 $vbox->add (new CFPlus::UI::FancyFrame
1151 label => "Server Info", 1447 label => "Server Info",
1157 1453
1158sub client_setup { 1454sub client_setup {
1159 my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1]; 1455 my $table = new CFPlus::UI::Table expand => 1, col_expand => [0, 1];
1160 1456
1161 my $row = 0; 1457 my $row = 0;
1162
1163 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Chat Command");
1164 $table->add_at (1, $row++, my $saycmd = new CFPlus::UI::Entry
1165 text => $CFG->{say_command},
1166 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. "
1167 . "Usually you want to enter something like 'say' or 'shout' or 'gsay' here. "
1168 . "But you could also set it to <b>tell <i>playername</i></b> to only chat with that user.",
1169 on_changed => sub {
1170 my ($self, $value) = @_;
1171 $CFG->{say_command} = $value;
1172 0
1173 }
1174 );
1175 1458
1176 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day"); 1459 $table->add_at (0, $row, new CFPlus::UI::Label valign => 0, align => 1, text => "Tip of the day");
1177 $table->add_at (1, $row++, new CFPlus::UI::CheckBox 1460 $table->add_at (1, $row++, new CFPlus::UI::CheckBox
1178 state => $CFG->{show_tips}, 1461 state => $CFG->{show_tips},
1179 tooltip => "Show the <b>Tip of the day</b> window at startup?", 1462 tooltip => "Show the <b>Tip of the day</b> window at startup?",
1190 tooltip => "This is maximum number of messages remembered in the <b>Messages</b> window. If the server " 1473 tooltip => "This is maximum number of messages remembered in the <b>Messages</b> window. If the server "
1191 . "sends more messages than this number, older messages get removed to save memory and " 1474 . "sends more messages than this number, older messages get removed to save memory and "
1192 . "computing time. A value of <b>0</b> disables this feature, but that is not recommended.", 1475 . "computing time. A value of <b>0</b> disables this feature, but that is not recommended.",
1193 on_changed => sub { 1476 on_changed => sub {
1194 my ($self, $value) = @_; 1477 my ($self, $value) = @_;
1195 $LOGVIEW->{max_par} = $CFG->{logview_max_par} = $value*1; 1478 $MESSAGE_WINDOW->set_max_para ($CFG->{logview_max_par} = $value*1);
1196 0 1479 0
1197 }, 1480 },
1198 ); 1481 );
1199 1482
1200 $table 1483 $table
1201}
1202
1203sub message_window {
1204 my $window = new CFPlus::UI::Toplevel
1205 name => "message_window",
1206 title => "Messages",
1207 border_bg => [1, 1, 1, 1],
1208 x => "max",
1209 y => 0,
1210 force_w => $::WIDTH * 0.4,
1211 force_h => $::HEIGHT * 0.5,
1212 child => (my $vbox = new CFPlus::UI::VBox),
1213 has_close_button => 1;
1214
1215 $vbox->add ($LOGVIEW);
1216
1217 $vbox->add (my $input = new CFPlus::UI::Entry
1218 tooltip => "<b>Chat Box</b>. If you enter a text and press return/enter here, the current <i>communication command</i> "
1219 . "from the client setup will be prepended (e.g. <b>shout</b>, <b>chat</b>...). "
1220 . "If you prepend a slash (/), you will submit a command instead (similar to IRC). "
1221 . "A better way to submit commands (and the occasional chat command) is often the map command completer.",
1222 on_focus_in => sub {
1223 my ($input, $prev_focus) = @_;
1224
1225 delete $input->{refocus_map};
1226
1227 if ($prev_focus == $MAPWIDGET && $input->{auto_activated}) {
1228 $input->{refocus_map} = 1;
1229 }
1230 delete $input->{auto_activated};
1231
1232 0
1233 },
1234 on_activate => sub {
1235 my ($input, $text) = @_;
1236 $input->set_text ('');
1237
1238 if ($text =~ /^\/(.*)/) {
1239 $::CONN->user_send ($1);
1240 } else {
1241 my $say_cmd = $::CFG->{say_command} || 'say';
1242 $::CONN->user_send ("$say_cmd $text");
1243 }
1244 if ($input->{refocus_map}) {
1245 delete $input->{refocus_map};
1246 $MAPWIDGET->focus_in
1247 }
1248
1249 0
1250 },
1251 on_escape => sub {
1252 $MAPWIDGET->grab_focus;
1253
1254 0
1255 },
1256 );
1257
1258 $CONSOLE = {
1259 window => $window,
1260 input => $input,
1261 };
1262
1263 $window
1264} 1484}
1265 1485
1266sub autopickup_setup { 1486sub autopickup_setup {
1267 my $r = new CFPlus::UI::ScrolledWindow ( 1487 my $r = new CFPlus::UI::ScrolledWindow (
1268 expand => 1, 1488 expand => 1,
1417 $sw1->add ($INV = new CFPlus::UI::Inventory); 1637 $sw1->add ($INV = new CFPlus::UI::Inventory);
1418 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}}); 1638 $INV->set_sort_order ($SORT_ORDER{$::CFG->{inv_sort}});
1419 1639
1420 $hb->add (my $vb2 = new CFPlus::UI::VBox); 1640 $hb->add (my $vb2 = new CFPlus::UI::VBox);
1421 1641
1422 $vb2->add ($INV_RIGHT_HB = new CFPlus::UI::HBox); 1642 $vb2->add ($INVR_HB = new CFPlus::UI::HBox);
1423 1643
1424 $vb2->add (my $sw2 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1); 1644 $vb2->add (my $sw2 = new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1425 $sw2->add ($INVR = new CFPlus::UI::Inventory); 1645 $sw2->add ($INVR = new CFPlus::UI::Inventory);
1426 1646
1427 # XXX: Call after $INVR = ... because set_opencont sets the items 1647 # XXX: Call after $INVR = ... because set_opencont sets the items
1428 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor"); 1648 CFPlus::Protocol::set_opencont ($::CONN, 0, "Floor");
1429 1649
1430 $hb 1650 $hb
1651}
1652
1653sub media_window {
1654 my $vb = new CFPlus::UI::VBox;
1655
1656 $vb->add (new CFPlus::UI::FancyFrame
1657 label => "Currently playing music",
1658 child => new CFPlus::UI::ScrolledWindow scroll_x => 1, scroll_y => 0,
1659 child => ($MUSIC_PLAYING_WIDGET = new CFPlus::UI::Label ellipsise => 0, fontsize => 0.8),
1660 );
1661
1662 $vb->add (new CFPlus::UI::FancyFrame
1663 label => "Other media used in this session",
1664 expand => 1,
1665 child => ($LICENSE_WIDGET = new CFPlus::UI::TextScroller
1666 expand => 1, fontsize => 0.8, padding_x => 4, padding_y => 4),
1667 );
1668
1669 $vb
1670}
1671
1672sub add_license {
1673 my ($meta) = @_;
1674
1675 $meta = $meta->{data}
1676 or return;
1677
1678 $meta->{license} || $meta->{author} || $meta->{source}
1679 or return;
1680
1681 $LICENSE_WIDGET->add_paragraph ({
1682 fg => [1, 1, 1, 1],
1683 markup => "<small>"
1684 . "<b>Name:</b> " . (CFPlus::asxml $meta->{name}) . "\n"
1685 . "<b>Author:</b> " . (CFPlus::asxml $meta->{author}) . "\n"
1686 . "<b>Source:</b> " . (CFPlus::asxml $meta->{source}) . "\n"
1687 . "<b>License:</b> " . (CFPlus::asxml $meta->{license}) . "\n"
1688 . "</small>",
1689 });
1690 $LICENSE_WIDGET->scroll_to_bottom;
1431} 1691}
1432 1692
1433sub toggle_player_page { 1693sub toggle_player_page {
1434 my ($widget) = @_; 1694 my ($widget) = @_;
1435 1695
1454 1714
1455 my $ntb = 1715 my $ntb =
1456 $PL_NOTEBOOK = 1716 $PL_NOTEBOOK =
1457 new CFPlus::UI::Notebook expand => 1; 1717 new CFPlus::UI::Notebook expand => 1;
1458 1718
1459 $ntb->add ( 1719 $ntb->add_tab (
1460 "Statistics (F2)" => $STATS_PAGE = stats_window, 1720 "Statistics (F2)" => $STATS_PAGE = stats_window,
1461 "Shows statistics, where all your Stats and Resistances are shown." 1721 "Shows statistics, where all your Stats and Resistances are shown."
1462 ); 1722 );
1463 $ntb->add ( 1723 $ntb->add_tab (
1464 "Skills (F3)" => $SKILL_PAGE = skill_window, 1724 "Skills (F3)" => $SKILL_PAGE = skill_window,
1465 "Shows all your Skills." 1725 "Shows all your Skills."
1466 ); 1726 );
1467 1727
1468 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1); 1728 my $spellsw = $SPELL_PAGE = new CFPlus::UI::ScrolledWindow (expand => 1, scroll_y => 1);
1469 $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList); 1729 $spellsw->add ($SPELL_LIST = new CFPlus::UI::SpellList);
1470 $ntb->add ( 1730 $ntb->add_tab (
1471 "Spellbook (F4)" => $spellsw, 1731 "Spellbook (F4)" => $spellsw,
1472 "Displays all spells you have and lets you edit keyboard shortcuts for them." 1732 "Displays all spells you have and lets you edit keyboard shortcuts for them."
1473 ); 1733 );
1474 $ntb->add ( 1734 $ntb->add_tab (
1475 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget, 1735 "Inventory (F5)" => $INVENTORY_PAGE = inventory_widget,
1476 "Toggles the inventory window, where you can manage your loot (or treasures :). " 1736 "Toggles the inventory window, where you can manage your loot (or treasures :). "
1477 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory." 1737 . "You can also hit the <b>Tab</b>-key to show/hide the Inventory."
1478 ); 1738 );
1479 $ntb->add (Pickup => autopickup_setup, 1739 $ntb->add_tab (Pickup => $PICKUP_PAGE = autopickup_setup,
1480 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them."); 1740 "Configure autopickup settings, i.e. which items you will pick up automatically when walking (or running) over them.");
1741
1742 $ntb->add_tab (Media => media_window,
1743 "License, Author and Source info for media sent by the server.");
1481 1744
1482 $ntb->set_current_page ($INVENTORY_PAGE); 1745 $ntb->set_current_page ($INVENTORY_PAGE);
1483 1746
1484 $plwin->add ($ntb); 1747 $plwin->add ($ntb);
1485 $plwin 1748 $plwin
1639 on_activate => sub { $QUIT_DIALOG->hide; 0 }, 1902 on_activate => sub { $QUIT_DIALOG->hide; 0 },
1640 ); 1903 );
1641 $hb->add (new CFPlus::UI::Button 1904 $hb->add (new CFPlus::UI::Button
1642 text => "Quit anyway", 1905 text => "Quit anyway",
1643 expand => 1, 1906 expand => 1,
1644 on_activate => sub { exit }, 1907 on_activate => sub { Event::unloop_all },
1645 ); 1908 );
1646 } 1909 }
1647 1910
1648 $QUIT_DIALOG->show; 1911 $QUIT_DIALOG->show;
1649 $QUIT_DIALOG->grab_focus; 1912 $QUIT_DIALOG->grab_focus;
1722 1985
1723 $SDL_ACTIVE = 1; 1986 $SDL_ACTIVE = 1;
1724 $LAST_REFRESH = time - 0.01; 1987 $LAST_REFRESH = time - 0.01;
1725 1988
1726 CFPlus::OpenGL::init; 1989 CFPlus::OpenGL::init;
1990 CFPlus::Macro::init;
1727 1991
1728 $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize}; 1992 $FONTSIZE = int $HEIGHT / 40 * $CFG->{gui_fontsize};
1729 1993
1730 $CFPlus::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d# 1994 $CFPlus::UI::ROOT->configure (0, 0, $WIDTH, $HEIGHT);#d#
1731 1995
1732 ############################################################################# 1996 #############################################################################
1733 1997
1734 if ($DEBUG_STATUS) { 1998 if ($DEBUG_STATUS) {
1735 CFPlus::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h; 1999 CFPlus::UI::rescale_widgets $WIDTH / $old_w, $HEIGHT / $old_h;
1736 } else { 2000 } else {
1737 # create the widgets 2001 # create/configure the widgets
2002
2003 $CFPlus::UI::ROOT->connect (key_down => sub {
2004 my (undef, $ev) = @_;
2005
2006 if (my @macros = CFPlus::Macro::find $ev) {
2007 CFPlus::Macro::execute $_ for @macros;
2008
2009 return 1;
2010 }
2011
2012 0
2013 });
1738 2014
1739 $DEBUG_STATUS = new CFPlus::UI::Label 2015 $DEBUG_STATUS = new CFPlus::UI::Label
1740 padding => 0, 2016 padding => 0,
1741 z => 100, 2017 z => 100,
1742 force_x => "max", 2018 force_x => "max",
1767 2043
1768 $MAPWIDGET = new CFPlus::MapWidget; 2044 $MAPWIDGET = new CFPlus::MapWidget;
1769 $MAPWIDGET->connect (activate_console => sub { 2045 $MAPWIDGET->connect (activate_console => sub {
1770 my ($mapwidget, $preset) = @_; 2046 my ($mapwidget, $preset) = @_;
1771 2047
1772 if ($CONSOLE) { 2048 $MESSAGE_WINDOW->activate_console ($preset)
1773 $CONSOLE->{input}->{auto_activated} = 1; 2049 if $MESSAGE_WINDOW;
1774 $CONSOLE->{input}->grab_focus;
1775
1776 if ($preset && $CONSOLE->{input}->get_text eq '') {
1777 $CONSOLE->{input}->set_text ($preset);
1778 }
1779 }
1780 }); 2050 });
1781 $MAPWIDGET->show; 2051 $MAPWIDGET->show;
1782 $MAPWIDGET->grab_focus; 2052 $MAPWIDGET->grab_focus;
1783 2053
1784 $LOGVIEW = new CFPlus::UI::TextScroller 2054 $COMPLETER = new CFPlus::MapWidget::Command::
1785 expand => 1, 2055 command => { },
1786 font => $FONT_FIXED, 2056 tooltip => "#completer_help",
1787 fontsize => $::CFG->{log_fontsize},
1788 indent => -4,
1789 can_hover => 1,
1790 can_events => 1,
1791 max_par => $CFG->{logview_max_par},
1792 tooltip => "<b>Server Log</b>. This text viewer contains all recent messages sent by the server.",
1793 ; 2057 ;
1794 2058
1795 $SETUP_DIALOG = new CFPlus::UI::Toplevel 2059 $SETUP_DIALOG = new CFPlus::UI::Toplevel
1796 title => "Setup", 2060 title => "Setup",
1797 name => "setup_dialog", 2061 name => "setup_dialog",
1802 force_h => $::HEIGHT * 0.6, 2066 force_h => $::HEIGHT * 0.6,
1803 has_close_button => 1, 2067 has_close_button => 1,
1804 ; 2068 ;
1805 2069
1806 $METASERVER = metaserver_dialog; 2070 $METASERVER = metaserver_dialog;
2071 $MESSAGE_WINDOW = new CFPlus::UI::MessageWindow;
1807 2072
1808 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1, 2073 $SETUP_DIALOG->add ($SETUP_NOTEBOOK = new CFPlus::UI::Notebook expand => 1, debug => 1,
1809 filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1); 2074 filter => new CFPlus::UI::ScrolledWindow expand => 1, scroll_y => 1);
1810 2075
1811 $SETUP_NOTEBOOK->add (Server => $SETUP_SERVER = server_setup, 2076 $SETUP_NOTEBOOK->add_tab (Server => $SETUP_SERVER = server_setup,
1812 "Configure the server to play on, your username, password and other server-related options."); 2077 "Configure the server to play on, your username, password and other server-related options.");
1813 $SETUP_NOTEBOOK->add (Client => client_setup, 2078 $SETUP_NOTEBOOK->add_tab (Client => client_setup,
1814 "Configure various client-specific settings."); 2079 "Configure various client-specific settings.");
1815 $SETUP_NOTEBOOK->add (Graphics => graphics_setup, 2080 $SETUP_NOTEBOOK->add_tab (Graphics => graphics_setup,
1816 "Configure the video mode, performance, fonts and other graphical aspects of the game."); 2081 "Configure the video mode, performance, fonts and other graphical aspects of the game.");
1817 $SETUP_NOTEBOOK->add (Audio => audio_setup, 2082 $SETUP_NOTEBOOK->add_tab (Audio => audio_setup,
1818 "Configure the use of audio, sound effects and background music."); 2083 "Configure the use of audio, sound effects and background music.");
1819 $SETUP_NOTEBOOK->add (Keyboard => $SETUP_KEYBOARD = keyboard_setup, 2084 $SETUP_NOTEBOOK->add_tab (Keyboard => $SETUP_KEYBOARD = keyboard_setup,
1820 "Lets you define, edit and delete key bindings." 2085 "Lets you define, edit and delete key bindings."
1821 . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor " 2086 . "There is a shortcut for making bindings: <b>Control-Insert</b> opens the binding editor "
1822 . "with nothing set and the recording started. After doing the actions you " 2087 . "with nothing set and the recording started. After doing the actions you "
1823 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. " 2088 . "want to record press <b>Insert</b> and you will be asked to press a key-combo. "
1824 . "After pressing the combo the binding will be saved automatically and the " 2089 . "After pressing the combo the binding will be saved automatically and the "
1825 . "binding editor closes"); 2090 . "binding editor closes");
1826 $SETUP_NOTEBOOK->add (Debug => debug_setup, 2091 $SETUP_NOTEBOOK->add_tab (Debug => debug_setup,
1827 "Some debuggin' options. Do not ask."); 2092 "Some debuggin' options. Do not ask.");
1828 2093
1829 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top 2094 $BUTTONBAR = new CFPlus::UI::Buttonbar x => 0, y => 0, z => 200; # put on top
1830 2095
1831 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG, 2096 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Setup", other => $SETUP_DIALOG,
1832 tooltip => "Toggles a dialog where you can configure all aspects of this client."); 2097 tooltip => "Toggles a dialog where you can configure all aspects of this client.");
1833 2098
1834 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW = message_window, 2099 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Message Window", other => $MESSAGE_WINDOW,
1835 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server."); 2100 tooltip => "Toggles the server message log, where the client collects <i>all</i> messages from the server.");
1836 2101
1837 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 2102 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
1838 2103
1839 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window, 2104 $BUTTONBAR->add (new CFPlus::UI::Flopper text => "Playerbook", other => player_window,
1859 tooltip => "Terminates the program", 2124 tooltip => "Terminates the program",
1860 on_activate => sub { 2125 on_activate => sub {
1861 if ($CONN) { 2126 if ($CONN) {
1862 open_quit_dialog; 2127 open_quit_dialog;
1863 } else { 2128 } else {
1864 exit; 2129 Event::unloop_all;
1865 } 2130 }
1866 0 2131 0
1867 }, 2132 },
1868 ); 2133 );
1869 2134
1870 $BUTTONBAR->show; 2135 $BUTTONBAR->show;
1871 $SETUP_DIALOG->show; 2136 $SETUP_DIALOG->show;
2137 $MESSAGE_WINDOW->show;
1872 } 2138 }
1873 2139
1874 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]); 2140 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
1875}
1876
1877sub setup_build_button {
1878 my ($enabled) = @_;
1879 if ($enabled) {
1880 $BUILD_BUTTON->hide if $BUILD_BUTTON;
1881 $BUILD_BUTTON ||= new CFPlus::UI::Button
1882 text => "Build",
1883 tooltip => "Opens the ingame builder",
1884 on_activate => sub {
1885 if ($CONN) {
1886 $CONN->send_ext_req (builder_player_items => sub {
1887 open_ingame_editor ($_[0]) if exists $_[0]->{items};
1888 });
1889 }
1890 0
1891 };
1892 $BUTTONBAR->add ($BUILD_BUTTON);
1893 } else {
1894 $BUILD_BUTTON->hide if $BUILD_BUTTON;
1895 }
1896}
1897
1898sub open_ingame_editor {
1899 my ($msg) = @_;
1900
1901 my $win = new CFPlus::UI::Toplevel
1902 x => 0,
1903 y => 'center',
1904 z => 4,
1905 name => 'builder_window',
1906 force_w => int $WIDTH * 1/4,
1907 force_h => int $HEIGHT * 3/4,
1908 title => "In game builder",
1909 has_close_button => 1;
1910
1911 my $r = new CFPlus::UI::ScrolledWindow (
1912 expand => 1,
1913 scroll_y => 1
1914 );
1915 $r->add (my $vb = new CFPlus::UI::VBox);
1916 $win->add ($r);
1917
1918
1919 $vb->add (
1920 new CFPlus::UI::Button
1921 text => "Disable build mode",
1922 on_activate => sub { $::IN_BUILD_MODE = undef }
1923 );
1924 $vb->add (
1925 new CFPlus::UI::Button
1926 text => "ERASE",
1927 on_activate => sub { $::IN_BUILD_MODE = { do_erase => 1 } }
1928 );
1929
1930 for my $itemarchname (
1931 sort {
1932 $msg->{items}->{$a}->{build_arch_name}
1933 cmp $msg->{items}->{$b}->{build_arch_name}
1934 } keys %{$msg->{items}}
1935 ) {
1936 my $info = $msg->{items}->{$itemarchname};
1937 $vb->add (
1938 new CFPlus::UI::Button text => $info->{build_arch_name},
1939 on_activate => sub {
1940 $::IN_BUILD_MODE = { item => $itemarchname, info => $info };
1941
1942 if (grep { $msg->{items}->{$itemarchname}->{$_} } qw/has_connection has_name has_text/) {
1943 build_mode_query_arch_info ();
1944 }
1945 }
1946 );
1947 }
1948
1949 $win->show;
1950}
1951
1952sub build_mode_query_arch_info {
1953 my ($iteminfo) = $::IN_BUILD_MODE;
1954 my $itemarchname = $iteminfo->{item};
1955 my $info = $iteminfo->{info};
1956
1957 my $dialog = new CFPlus::UI::Toplevel
1958 x => "center",
1959 y => "center",
1960 z => 50,
1961 force_w => int $WIDTH * 1/2,
1962 title => "Enter information for placement of '$itemarchname'",
1963 has_close_button => 1;
1964
1965 $dialog->add (my $vb = new CFPlus::UI::VBox expand => 1);
1966
1967 $vb->add (my $table = new CFPlus::UI::Table expand => 1);
1968 my $row = 0;
1969 if ($info->{has_name}) {
1970 $table->add_at (0, $row, new CFPlus::UI::Label text => "Name:");
1971 $table->add_at (1, $row++, new CFPlus::UI::Entry expand => 1, on_changed => sub { $::IN_BUILD_MODE->{name} = $_[1]; 0 });
1972 }
1973 if ($info->{has_text}) {
1974 $table->add_at (0, $row, new CFPlus::UI::Label text => "Text:");
1975 $table->add_at (1, $row++, new CFPlus::UI::Entry expand => 1, on_changed => sub { $::IN_BUILD_MODE->{text} = $_[1]; 0 });
1976 }
1977 if ($info->{has_connection}) {
1978 $table->add_at (0, $row, new CFPlus::UI::Label text => "Connection ID:");
1979 $table->add_at (1, $row++,
1980 new CFPlus::UI::Entry
1981 expand => 1,
1982 on_changed => sub { $::IN_BUILD_MODE->{connection} = $_[1]; 0 },
1983 tooltip => "Enter the connection ID here. The connection ID connects actors like a lever to a gate or a magic ear to a gate"
1984 );
1985 }
1986
1987 $vb->add (my $hb = new CFPlus::UI::HBox expand => 1);
1988 $hb->add (new CFPlus::UI::Button
1989 text => "Close",
1990 expand => 1,
1991 on_activate => sub { $dialog->hide; 0 },
1992 );
1993 $dialog->show;
1994} 2141}
1995 2142
1996sub video_shutdown { 2143sub video_shutdown {
1997 CFPlus::OpenGL::shutdown; 2144 CFPlus::OpenGL::shutdown;
1998 2145
1999 undef $SDL_ACTIVE; 2146 undef $SDL_ACTIVE;
2000} 2147}
2001 2148
2002sub audio_channel_finished {
2003 my ($channel) = @_;
2004
2005 #warn "channel $channel finished\n";#d#
2006}
2007
2008sub audio_music_set {
2009 my ($songs) = @_;
2010
2011 my @want =
2012 grep $_,
2013 map $CONN->{music_meta}{$_},
2014 @$songs;
2015
2016 if (@want) {
2017 @MUSIC_WANT = @want;
2018 &audio_music_changed ();
2019 }
2020}
2021
2022sub audio_music_start {
2023 my $path = $MUSIC_PLAYING->{path}
2024 or return;
2025
2026 CFPlus::DB::prefetch_file $path, 1024_000, sub {
2027 # music might have changed...
2028 $path eq $MUSIC_PLAYING->{path}
2029 or return &audio_music_start ();
2030
2031 $MUSIC_PLAYER = new_from_file CFPlus::MixMusic $path;
2032
2033 my $NOW = time;
2034
2035 if ($MUSIC_PLAYING->{stop_time} > $NOW - $MUSIC_RESUME) {
2036 my $pos = $MUSIC_PLAYING->{stop_pos};
2037 $MUSIC_PLAYER->fade_in_pos (0, 1000, $pos);
2038 $MUSIC_START = time - $pos;
2039 } else {
2040 $MUSIC_PLAYER->play (0);
2041 $MUSIC_START = time;
2042 }
2043
2044 delete $MUSIC_PLAYING->{stop_time};
2045 delete $MUSIC_PLAYING->{stop_pos};
2046 }
2047}
2048
2049sub audio_music_changed {
2050 return unless $CFG->{bgm_enable};
2051
2052 # default MUSIC_WANT == MUSIC_DEFAULT
2053 @MUSIC_WANT = { path => CFPlus::find_rcfile "music/$MUSIC_DEFAULT" } unless @MUSIC_WANT;
2054
2055 # if the currently playing song is acceptable, let it continue
2056 return if $MUSIC_PLAYING
2057 && grep $MUSIC_PLAYING->{path} eq $_->{path}, @MUSIC_WANT;
2058
2059 my $NOW = time;
2060
2061 if ($MUSIC_PLAYING) {
2062 $MUSIC_PLAYING->{stop_time} = $NOW;
2063 $MUSIC_PLAYING->{stop_pos} = $NOW - $MUSIC_START;
2064 CFPlus::MixMusic::fade_out 1000;
2065 } else {
2066 # sort by stop time, oldest first
2067 @MUSIC_WANT = sort { $a->{stop_time} <=> $b->{stop_time} } @MUSIC_WANT;
2068
2069 # if the most recently-played piece played very recently,
2070 # resume it, else choose the oldest piece for rotation.
2071 $MUSIC_PLAYING =
2072 $MUSIC_WANT[-1]{stop_time} > $NOW - $MUSIC_RESUME
2073 ? $MUSIC_WANT[-1]
2074 : $MUSIC_WANT[0];
2075
2076 audio_music_start;
2077 }
2078}
2079
2080sub audio_music_finished {
2081 $MUSIC_PLAYING = undef;
2082 undef $MUSIC_PLAYER;
2083
2084 audio_music_changed;
2085}
2086
2087sub audio_init {
2088 if ($CFG->{audio_enable}) {
2089 if (open my $fh, "<", CFPlus::find_rcfile "sounds/config") {
2090 $SDL_MIXER = !CFPlus::Mix_OpenAudio;
2091
2092 unless ($SDL_MIXER) {
2093 status "Unable to open sound device: there will be no sound";
2094 return;
2095 }
2096
2097 CFPlus::Mix_AllocateChannels 8;
2098 CFPlus::MixMusic::volume $CFG->{bgm_volume} * 128;
2099
2100 audio_music_finished;
2101
2102 local $_;
2103 while (<$fh>) {
2104 next if /^\s*#/;
2105 next if /^\s*$/;
2106
2107 my ($file, $volume, $event) = split /\s+/, $_, 3;
2108
2109 push @SOUNDS, "$volume,$file";
2110
2111 $AUDIO_CHUNKS{"$volume,$file"} ||= do {
2112 my $chunk = new_from_file CFPlus::MixChunk CFPlus::find_rcfile "sounds/$file";
2113 $chunk->volume ($volume * 128 / 100);
2114 $chunk
2115 };
2116 }
2117 } else {
2118 status "unable to open sound config: $!";
2119 }
2120 }
2121}
2122
2123sub audio_shutdown {
2124 CFPlus::Mix_CloseAudio if $SDL_MIXER;
2125 undef $SDL_MIXER;
2126 @SOUNDS = ();
2127 %AUDIO_CHUNKS = ();
2128}
2129
2130my %animate_object; 2149my %animate_object;
2131my $animate_timer; 2150my $animate_timer;
2132 2151
2133my $fps = 9; 2152my $fps = 9;
2134 2153
2135my %demo;#d#
2136
2137sub force_refresh { 2154sub force_refresh {
2155 if ($ENV{CFPLUS_DEBUG} & 4) {
2138 $fps = $fps * 0.95 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.05; 2156 $fps = $fps * 0.98 + 1 / (($NOW - $LAST_REFRESH) || 0.1) * 0.02;
2139 debug sprintf "%3.2f", $fps if $ENV{CFPLUS_DEBUG} & 4; 2157 debug sprintf "%3.2f", $fps;
2158 }
2140 2159
2141 $CFPlus::UI::ROOT->draw; 2160 $CFPlus::UI::ROOT->draw;
2142 2161 CFPlus::SDL_GL_SwapBuffers;
2143 $WANT_REFRESH = 0;
2144 $CAN_REFRESH = 0;
2145 $LAST_REFRESH = $NOW; 2162 $LAST_REFRESH = $NOW;
2146 2163 $WANT_REFRESH->stop;
2147 CFPlus::SDL_GL_SwapBuffers;
2148} 2164}
2149 2165
2166$WANT_REFRESH = Event->idle (min => 0.001, max => 0.06, parked => 1, cb => \&force_refresh);
2167
2150my $refresh_watcher = Event->timer (after => 0, hard => 0, interval => 1 / $MAX_FPS, cb => sub { 2168my $input = Event->timer (after => 0, hard => 0, interval => 1 / 50, cb => sub {
2151 $NOW = time; 2169 $NOW = time;
2152 2170
2153 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_) 2171 ($SDL_CB{$_->{type}} || sub { warn "unhandled event $_->{type}" })->($_)
2154 for CFPlus::poll_events; 2172 for CFPlus::poll_events;
2155 2173
2156 if (%animate_object) { 2174 if (%animate_object) {
2157 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object; 2175 $_->animate ($LAST_REFRESH - $NOW) for values %animate_object;
2158 ++$WANT_REFRESH; 2176 $WANT_REFRESH->start;
2159 }
2160
2161 if ($WANT_REFRESH) {
2162 force_refresh;
2163 } else {
2164 $CAN_REFRESH = 1;
2165 } 2177 }
2166}); 2178});
2167 2179
2168sub animation_start { 2180sub animation_start {
2169 my ($widget) = @_; 2181 my ($widget) = @_;
2173sub animation_stop { 2185sub animation_stop {
2174 my ($widget) = @_; 2186 my ($widget) = @_;
2175 delete $animate_object{$widget}; 2187 delete $animate_object{$widget};
2176} 2188}
2177 2189
2178# check once/second for faces that need to be prefetched
2179# this should, of course, only run on demand, but
2180# SDL forces worse things on us....
2181
2182Event->timer (after => 1, interval => 0.25, cb => sub {
2183 $CONN->face_prefetch
2184 if $CONN;
2185});
2186
2187%SDL_CB = ( 2190%SDL_CB = (
2188 CFPlus::SDL_QUIT => sub { 2191 CFPlus::SDL_QUIT => sub {
2189 exit; 2192 Event::unloop_all;
2190 }, 2193 },
2191 CFPlus::SDL_VIDEORESIZE => sub { 2194 CFPlus::SDL_VIDEORESIZE => sub {
2192 }, 2195 },
2193 CFPlus::SDL_VIDEOEXPOSE => sub { 2196 CFPlus::SDL_VIDEOEXPOSE => sub {
2194 CFPlus::UI::full_refresh; 2197 CFPlus::UI::full_refresh;
2233 CFPlus::DB::Server::run; 2236 CFPlus::DB::Server::run;
2234 2237
2235 CFPlus::UI::set_layout ($::CFG->{layout}); 2238 CFPlus::UI::set_layout ($::CFG->{layout});
2236 2239
2237 my %DEF_CFG = ( 2240 my %DEF_CFG = (
2238 sdl_mode => 0, 2241 sdl_mode => 0,
2239 width => 640,
2240 height => 480,
2241 fullscreen => 0, 2242 fullscreen => 0,
2242 fast => 0, 2243 fast => 0,
2244 force_opengl11 => undef,
2245 texture_compression => 1,
2243 map_scale => 1, 2246 map_scale => 1,
2244 fow_enable => 1, 2247 fow_enable => 1,
2245 fow_intensity => 0, 2248 fow_intensity => 0,
2246 map_smoothing => 1, 2249 map_smoothing => 1,
2247 gui_fontsize => 1, 2250 gui_fontsize => 1,
2248 log_fontsize => 0.7, 2251 log_fontsize => 0.7,
2249 gauge_fontsize => 1, 2252 gauge_fontsize => 1,
2250 gauge_size => 0.35, 2253 gauge_size => 0.35,
2251 stat_fontsize => 0.7, 2254 stat_fontsize => 0.7,
2252 mapsize => 100, 2255 mapsize => 100,
2253 say_command => 'chat',
2254 audio_enable => 1, 2256 audio_enable => 1,
2257 effects_enable => 1,
2258 effects_volume => 1,
2255 bgm_enable => 1, 2259 bgm_enable => 1,
2256 bgm_volume => 0.25, 2260 bgm_volume => 0.5,
2257 face_prefetch => 0,
2258 output_sync => 1, 2261 output_sync => 1,
2259 output_count => 1, 2262 output_count => 1,
2260 output_rate => "", 2263 output_rate => "",
2261 pickup => 0, 2264 pickup => 0,
2262 inv_sort => "mtime", 2265 inv_sort => "mtime",
2263 default => "profile", # default profile 2266 default => "profile", # default profile
2264 show_tips => 1, 2267 show_tips => 1,
2265 logview_max_par => 1000, 2268 logview_max_par => 1000,
2266 ); 2269 );
2267 2270
2268 while (my ($k, $v) = each %DEF_CFG) { 2271 while (my ($k, $v) = each %DEF_CFG) {
2269 $CFG->{$k} = $v unless exists $CFG->{$k}; 2272 $CFG->{$k} = $v unless exists $CFG->{$k};
2270 } 2273 }
2327# } 2330# }
2328# my $t2 = Time::HiRes::time; 2331# my $t2 = Time::HiRes::time;
2329# warn $t2-$t1; 2332# warn $t2-$t1;
2330# } 2333# }
2331 2334
2332 $startup_done->();
2333
2334 video_init; 2335 video_init;
2335 audio_init; 2336 audio_init;
2336} 2337}
2337 2338
2338show_tip_of_the_day if $CFG->{show_tips}; 2339show_tip_of_the_day if $CFG->{show_tips};
2339 2340
2341Event->idle (cb => sub {
2342 $_[0]->w->cancel;
2343 $startup_done->();
2344});
2345
2340Event::loop; 2346Event::loop;
2347
2348#video_shutdown;
2349#audio_shutdown;
2341#CFPlus::SDL_Quit; 2350CFPlus::SDL_Quit;
2342#CFPlus::_exit 0;
2343
2344END {
2345 CFPlus::SDL_Quit;
2346 CFPlus::DB::Server::stop; 2351CFPlus::DB::Server::stop;
2347}
2348 2352
2349=head1 NAME 2353=head1 NAME
2350 2354
2351cfplus - A Crossfire+ and Crossfire game client 2355cfplus - A Crossfire TRT and Crossfire game client
2352 2356
2353=head1 SYNOPSIS 2357=head1 SYNOPSIS
2354 2358
2355Just run it - no commandline arguments are supported. 2359Just run it - no commandline arguments are supported.
2356 2360
2357=head1 USAGE 2361=head1 USAGE
2358 2362
2359cfplus utilises OpenGL for all UI elements and the game. It is supposed to be used 2363cfplus utilises OpenGL for all UI elements and the game. It is supposed to
2360fullscreen and interactively. 2364be used in fullscreen mode and interactively.
2361 2365
2362=head1 DEBUGGING 2366=head1 DEBUGGING
2363 2367
2364 2368
2365CFPLUS_DEBUG - environment variable 2369CFPLUS_DEBUG - environment variable

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines