#! perl # mandatory # this extension provides "environmental effects", # meaning mostly background music and region messages right now. our %MUSIC_GROUP; 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} }; } 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_FACE_CACHE; # cleared by reload_facedata 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; $pl->ob->active or next; my $ns = $pl->ns or next; $ns->extcmd or next; my $map = $pl->ob->map or next; my $faces = delete $ns->{music_play_once}; $faces ||= $MUSIC_FACE_CACHE{$map} ||= do { # 1. map-specific music info parse_musiclist $map->{music} or do { # 2. fall back to region if no map-specific music my $rgn = $pl->ob->region or next; my $par = $rgn; while () { last if exists $par->{music}; $par = $par->parent or last; } parse_musiclist $par->{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; }