#! perl # mandatory # this extension provides "environmental effects", # meaning mostly background music and region messages right now. our %MUSIC_GROUP; our %MUSIC_FACE_CACHE; # cleared by reload_facedata sub reload { cf::trace "loading music config from $DATADIR/music\n"; 0 < Coro::AIO::aio_load "$DATADIR/music", my $data or die "$DATADIR/music $!"; my $conf = JSON::XS->new->utf8->relaxed->decode ($data); %MUSIC_GROUP = %{ $conf->{group} }; %MUSIC_FACE_CACHE = (); } cf::post_init { $_[0] ? cf::async { reload } : reload }; sub parse_musiclist($) { my ($list) = @_; return undef unless defined $list; my @name = split /\s*,\s*/, $list; my @face; while (@name) { my $name = shift @name; if ($name =~ /^\@(.*)$/) { my $group = $MUSIC_GROUP{$1} or cf::error "music group $1 does not exist (referenced in '$list')"; unshift @name, @$group; } else { my $face = cf::face::find "music/$name" or "unable to find music face $name (referenced in '$list')"; push @face, $face if $face; } } # sort by size, smallest first, because it'S the fastest to download... [ sort { (cf::face::get_data_size $a) <=> (cf::face::get_data_size $b) } @face ] } our %MUSIC_QUEUE; our $MUSIC_SCHEDULER = cf::async_ext { $Coro::current->{desc} = "music scheduler"; while () { for (keys %MUSIC_QUEUE) { delete $MUSIC_QUEUE{$_}; cf::get_slot 0.01, -10, "music scheduler"; my $pl = cf::player::find_active $_ or next; my $ob = $pl->ob; my $ns = $pl->ns or next; my $map = $ob->map; my $rgn = $ob->region; my $id = join "\x00", $map->path, $rgn->name; my $faces = delete $ns->{music_play_once}; $faces ||= $MUSIC_FACE_CACHE{$id} ||= do { # 1. map-specific music info parse_musiclist $map->{music} or do { # 2. fall back to region if no map-specific music $rgn = $rgn->parent while $rgn && !exists $rgn->{music}; parse_musiclist $rgn->{music} } ; }; $faces or next; my $facestr = join ",", @$faces; $ns->{current_music_faces} ne $facestr or next; $ns->{current_music_faces} = $facestr; my $pri = 0; $ns->send_face ($_, --$pri - 110) for @$faces; $ns->flush_fx; $ns->ext_msg (ambient_music => $faces); } Coro::schedule unless %MUSIC_QUEUE; } }; cf::player->attach ( on_region_change => sub { my ($pl, $new, $old) = @_; $pl->ob->message ("You are now " . $new->longname . ". H", $new->longname); undef $MUSIC_QUEUE{$pl->ob->name}; $MUSIC_SCHEDULER->ready; }, on_map_change => sub { my ($pl, $new) = @_; undef $MUSIC_QUEUE{$pl->ob->name}; $MUSIC_SCHEDULER->ready; }, ); sub play_music_once { my ($pl, $music) = @_; $pl->ns->{music_play_once} = parse_musiclist $music; undef $MUSIC_QUEUE{$pl->ob->name}; $MUSIC_SCHEDULER->ready; }