123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473 |
- #!/usr/bin/env perl
- #
- #############################################################################
- # Asciiquarium - An aquarium animation in ASCII art
- #
- # This program displays an aquarium/sea animation using ASCII art.
- # It requires the module Term::Animation, which requires Curses. You
- # can get both modules from http://search.cpan.org. Asciiquarium will
- # only run on platforms with a curses library, so Windows is not supported.
- #
- # The current version of this program is available at:
- #
- # http://robobunny.com/projects/asciiquarium
- #
- #############################################################################
- # Author:
- # Kirk Baucom <kbaucom@schizoid.com>
- #
- # Contributors:
- # Joan Stark: http://www.geocities.com/SoHo/7373/
- # most of the ASCII art
- #
- # License:
- #
- # Copyright (C) 2013 Kirk Baucom (kbaucom@schizoid.com)
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- #############################################################################
- use Term::Animation 2.0;
- use Term::Animation::Entity;
- use Data::Dumper;
- use Curses;
- use strict;
- use warnings;
- my $version = "1.1";
- my @random_objects = init_random_objects();
- # the Z depth at which certain items occur
- my %depth = (
- # no gui yet
- guiText => 0,
- gui => 1,
- # under water
- shark => 2,
- fish_start => 3,
- fish_end => 20,
- seaweed => 21,
- castle => 22,
- # waterline
- water_line3 => 2,
- water_gap3 => 3,
- water_line2 => 4,
- water_gap2 => 5,
- water_line1 => 6,
- water_gap1 => 7,
- water_line0 => 8,
- water_gap0 => 9,
- );
- main();
- ####################### MAIN #######################
- sub main {
- my $anim = Term::Animation->new();
- # set the wait time for getch
- halfdelay(1);
- #nodelay(1);
- $anim->color(1);
- my $start_time = time;
- my $paused = 0;
- while(1) {
- add_environment($anim);
- add_castle($anim);
- add_all_seaweed($anim);
- add_all_fish($anim);
- random_object(undef, $anim);
-
- $anim->redraw_screen();
- my $nexttime = 0;
- while(1) {
- my $in = getch();
- if ( $in eq 'q' ) { quit(); } # Exit
- elsif( $in eq 'r' || $in eq KEY_RESIZE()) { last; } # Redraw (will recreate all objects)
- elsif( $in eq 'p' ) { $paused = !$paused; }
- $anim->animate() unless($paused);
- }
- $anim->update_term_size();
- $anim->remove_all_entities();
- }
- }
- sub add_environment {
- my ($anim) = @_;
- my @water_line_segment = (
- q{~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~},
- q{^^^^ ^^^ ^^^ ^^^ ^^^^ },
- q{^^^^ ^^^^ ^^^ ^^ },
- q{^^ ^^^^ ^^^ ^^^^^^ }
- );
- # tile the segments so they stretch across the screen
- my $segment_size = length($water_line_segment[0]);
- my $segment_repeat = int($anim->width()/$segment_size) + 1;
- foreach my $i (0..$#water_line_segment) {
- $water_line_segment[$i] = $water_line_segment[$i]x$segment_repeat;
- }
- foreach my $i (0..$#water_line_segment) {
- $anim->new_entity(
- name => "water_seg_$i",
- type => "waterline",
- shape => $water_line_segment[$i],
- position => [ 0, $i+5, $depth{'water_line' . $i} ],
- default_color => 'cyan',
- depth => 22,
- physical => 1,
- );
- }
- }
- sub add_castle {
- my ($anim) = @_;
- my $castle_image = q{
- T~~
- |
- /^\
- / \
- _ _ _ / \ _ _ _
- [ ]_[ ]_[ ]/ _ _ \[ ]_[ ]_[ ]
- |_=__-_ =_|_[ ]_[ ]_|_=-___-__|
- | _- = | =_ = _ |= _= |
- |= -[] |- = _ = |_-=_[] |
- | =_ |= - ___ | =_ = |
- |= []- |- /| |\ |=_ =[] |
- |- =_ | =| | | | |- = - |
- |_______|__|_|_|_|__|_______|
- };
- my $castle_mask = q{
- RR
- yyy
- y y
- y y
- y y
- yyy
- yy yy
- y y y y
- yyyyyyy
- };
- $anim->new_entity(
- name => "castle",
- shape => $castle_image,
- color => $castle_mask,
- position => [ $anim->width()-32, $anim->height()-13, $depth{'castle'} ],
- default_color => 'BLACK',
- );
- }
- sub add_all_seaweed {
- my ($anim) = @_;
- # figure out how many seaweed to add by the width of the screen
- my $seaweed_count = int($anim->width() / 15);
- for (1..$seaweed_count) {
- add_seaweed(undef, $anim);
- }
- }
- sub add_seaweed {
- my ($old_seaweed, $anim) = @_;
- my @seaweed_image = ('','');
- my $height = int(rand(4)) + 3;
- for my $i (1..$height) {
- my $left_side = $i%2;
- my $right_side = !$left_side;
- $seaweed_image[$left_side] .= "(\n";
- $seaweed_image[$right_side] .= " )\n";
- }
- my $x = int(rand($anim->width()-2)) + 1;
- my $y = $anim->height() - $height;
- my $anim_speed = rand(.05) + .25;
- $anim->new_entity(
- name => 'seaweed' . rand(1),
- shape => \@seaweed_image,
- position => [ $x, $y, $depth{'seaweed'} ],
- callback_args => [ 0, 0, 0, $anim_speed ],
- die_time => time() + int(rand(4*60)) + (8*60), # seaweed lives for 8 to 12 minutes
- death_cb => \&add_seaweed,
- default_color => 'green',
- );
- }
- # add an air bubble to a fish
- sub add_bubble {
- my ($fish, $anim) = @_;
- my $cb_args = $fish->callback_args();
- my @fish_size = $fish->size();
- my @fish_pos = $fish->position();
- my @bubble_pos = @fish_pos;
- # moving right
- if($cb_args->[0] > 0) {
- $bubble_pos[0] += $fish_size[0];
- }
- $bubble_pos[1] += int($fish_size[1] / 2);
- # bubble always goes on top of the fish
- $bubble_pos[2]--;
- $anim->new_entity(
- shape => [ '.', 'o', 'O', 'O', 'O' ],
- type => 'bubble',
- position => \@bubble_pos,
- callback_args => [ 0, -1, 0, .1 ],
- die_offscreen => 1,
- physical => 1,
- coll_handler => \&bubble_collision,
- default_color => 'CYAN',
- );
- }
- sub bubble_collision {
- my ($bubble, $anim) = @_;
- my $collisions = $bubble->collisions();
- foreach my $col_obj (@{$collisions}) {
- if($col_obj->type eq 'waterline') {
- $bubble->kill();
- last;
- }
- }
- }
- sub add_all_fish {
- my ($anim) = @_;
- # figure out how many fish to add by the size of the screen,
- # minus the stuff above the water
- my $screen_size = ($anim->height() - 9) * $anim->width();
- my $fish_count = int($screen_size / 350);
- for (1..$fish_count) {
- add_fish(undef, $anim);
- }
- }
- sub add_fish {
- my ($old_fish, $anim) = @_;
- my @fish_image = (
- q{
- \
- ...\..,
- \ /' \
- >= ( ' >
- / \ / /
- `"'"'/''
- },
- q{
- 2
- 1112111
- 6 11 1
- 66 7 4 5
- 6 1 3 1
- 11111311
- },
- q{
- /
- ,../...
- / '\ /
- < ' ) =<
- \ \ / \
- `'\'"'"'
- },
- q{
- 2
- 1112111
- 1 11 6
- 5 4 7 66
- 1 3 1 6
- 11311111
- },
- q{
- \
- \ /--\
- >= (o>
- / \__/
- /
- },
- q{
- 2
- 6 1111
- 66 745
- 6 1111
- 3
- },
- q{
- /
- /--\ /
- <o) =<
- \__/ \
- \
- },
- q{
- 2
- 1111 6
- 547 66
- 1111 6
- 3
- },
- q{
- \:.
- \;, ,;\\\\\,,
- \\\\\;;:::::::o
- ///;;::::::::<
- /;` ``/////``
- },
- q{
- 222
- 666 1122211
- 6661111111114
- 66611111111115
- 666 113333311
- },
- q{
- .:/
- ,,///;, ,;/
- o:::::::;;///
- >::::::::;;\\\\\
- ''\\\\\\\\\'' ';\
- },
- q{
- 222
- 1122211 666
- 4111111111666
- 51111111111666
- 113333311 666
- },
- q{
- __
- ><_'>
- '
- },
- q{
- 11
- 61145
- 3
- },
- q{
- __
- <'_><
- `
- },
- q{
- 11
- 54116
- 3
- },
- q{
- ..\,
- >=' ('>
- '''/''
- },
- q{
- 1121
- 661 745
- 111311
- },
- q{
- ,/..
- <') `=<
- ``\```
- },
- q{
- 1211
- 547 166
- 113111
- },
- q{
- \
- / \
- >=_('>
- \_/
- /
- },
- q{
- 2
- 1 1
- 661745
- 111
- 3
- },
- q{
- /
- / \
- <')_=<
- \_/
- \
- },
- q{
- 2
- 1 1
- 547166
- 111
- 3
- },
- q{
- ,\
- >=('>
- '/
- },
- q{
- 12
- 66745
- 13
- },
- q{
- /,
- <')=<
- \`
- },
- q{
- 21
- 54766
- 31
- },
- q{
- __
- \/ o\
- /\__/
- },
- q{
- 11
- 61 41
- 61111
- },
- q{
- __
- /o \/
- \__/\
- },
- q{
- 11
- 14 16
- 11116
- },
- );
-
- # 1: body
- # 2: dorsal fin
- # 3: flippers
- # 4: eye
- # 5: mouth
- # 6: tailfin
- # 7: gills
- my @colors = ('c','C','r','R','y','Y','b','B','g','G','m','M');
- my $fish_num = int(rand($#fish_image/2));
- my $fish_index = $fish_num * 2;
- my $speed = rand(2) + .25;
- my $depth = int(rand($depth{'fish_end'} - $depth{'fish_start'})) + $depth{'fish_start'};
- my $color_mask = $fish_image[$fish_index+1];
- $color_mask =~ s/4/W/gm;
- $color_mask = rand_color($color_mask);
- if($fish_num % 2) {
- $speed *= -1;
- }
- my $fish_object = Term::Animation::Entity->new(
- type => 'fish',
- shape => $fish_image[$fish_index],
- auto_trans => 1,
- color => $color_mask,
- position => [ 0, 0, $depth ],
- callback => \&fish_callback,
- callback_args => [ $speed, 0, 0 ],
- die_offscreen => 1,
- death_cb => \&add_fish,
- physical => 1,
- coll_handler => \&fish_collision,
- );
- my $max_height = 9;
- my $min_height = $anim->height() - $fish_object->{'HEIGHT'};
- $fish_object->{'Y'} = int(rand($min_height - $max_height)) + $max_height;
- if($fish_num % 2) {
- $fish_object->{'X'} = $anim->width()-2;
- } else {
- $fish_object->{'X'} = 1 - $fish_object->{'WIDTH'};
- }
- $anim->add_entity($fish_object);
- }
- sub fish_callback {
- my ($fish, $anim) = @_;
- if(int(rand(100)) > 97) {
- add_bubble($fish, $anim);
- }
- return $fish->move_entity($anim);
- }
- sub fish_collision {
- my ($fish, $anim) = @_;
- my $collisions = $fish->collisions();
- foreach my $col_obj (@{$collisions}) {
- if($col_obj->type eq 'teeth') {
- add_splat($anim, $col_obj->position());
- $fish->kill();
- last;
- } elsif($col_obj->type eq 'hook_point') {
- retract($col_obj);
- retract($fish);
- # get the hook and line
- my $hook = $anim->get_entities_of_type('fishhook')->[0];
- my $line = $anim->get_entities_of_type('fishline')->[0];
- retract($anim->entity($hook));
- retract($anim->entity($line));
- last;
- }
- }
- }
- sub add_splat {
- my ($anim, $x, $y, $z) = @_;
- my @splat_image = (
- q#
- .
- ***
- '
- #,
- q#
- ",*;`
- "*,**
- *"'~'
- #,
- q#
- , ,
- " ","'
- *" *'"
- " ; .
- #,
- q#
- * ' , ' `
- ' ` * . '
- ' `' ",'
- * ' " * .
- " * ', '
- #,
- );
- $anim->new_entity(
- shape => \@splat_image,
- position => [ $x - 4, $y - 2, $z-2 ],
- default_color => 'RED',
- callback_args => [ 0, 0, 0, .25 ],
- transparent => ' ',
- die_frame => 15,
- );
- }
- sub add_shark {
- my ($old_ent, $anim) = @_;
- my @shark_image = (
- q#
- __
- ( `\
- ,??????????????????????????) `\
- ;' `.????????????????????????( `\__
- ; `.?????????????__..---'' `~~~~-._
- `. `.____...--'' (b `--._
- > _.-' .(( ._ )
- .`.-`--...__ .-' -.___.....-(|/|/|/|/'
- ;.'?????????`. ...----`.___.',,,_______......---'
- '???????????'-'
- #,
- q#
- __
- /' )
- /' (??????????????????????????,
- __/' )????????????????????????.' `;
- _.-~~~~' ``---..__?????????????.' ;
- _.--' b) ``--...____.' .'
- ( _. )). `-._ <
- `\|\|\|\|)-.....___.- `-. __...--'-.'.
- `---......_______,,,`.___.'----... .'?????????`.;
- `-`???????????`
- #,
- );
- my @shark_mask = (
- q#
- cR
-
- cWWWWWWWW
- #,
- q#
- Rc
- WWWWWWWWc
- #,
- );
- my $dir = int(rand(2));
- my $x = -53;
- my $y = int(rand($anim->height() - (10 + 9))) + 9;
- my $teeth_x = -9;
- my $teeth_y = $y + 7;
- my $speed = 2;
- if($dir) {
- $speed *= -1;
- $x = $anim->width()-2;
- $teeth_x = $x + 9;
- }
- $anim->new_entity(
- type => 'teeth',
- shape => "*",
- position => [ $teeth_x, $teeth_y, $depth{'shark'}+1 ],
- depth => $depth{'fish_end'} - $depth{'fish_start'},
- callback_args => [ $speed, 0, 0 ],
- physical => 1,
- );
- $anim->new_entity(
- type => "shark",
- color => $shark_mask[$dir],
- shape => $shark_image[$dir],
- auto_trans => 1,
- position => [ $x, $y, $depth{'shark'} ],
- default_color => 'WHITE',
- callback_args => [ $speed, 0, 0 ],
- die_offscreen => 1,
- death_cb => sub { group_death(@_, 'teeth') },
- default_color => 'CYAN',
- );
- }
- # when a shark dies, kill the "teeth" too, the associated
- # entity that does the actual collision
- sub group_death {
- my ($entity, $anim, @bound_types) = @_;
- foreach my $type (@bound_types) {
- my $bound_entities = $anim->get_entities_of_type($type);
- foreach my $obj (@{$bound_entities}) {
- $anim->del_entity($obj);
- }
- }
- random_object($entity, $anim);
- }
- # pull the fishhook, line and whatever got caught back
- # to the surface
- sub retract {
- my ($entity) = @_;
- $entity->physical(0);
- if($entity->type eq 'fish') {
- my @pos = $entity->position();
- $pos[2] = $depth{'water_gap2'};
- $entity->position( @pos );
- $entity->callback( \&fishhook_cb );
- } else {
- $entity->callback_args( 'hooked' );
- }
- }
- # move the fishhook
- sub fishhook_cb {
- my ($entity, $anim) = @_;
- my @pos = $entity->position;
-
- # this means we hooked something, reel it in
- if(defined($entity->callback_args())) {
- $pos[1]--;
-
- # otherwise, just lower until we reach 1/4 from the bottom
- } else {
- if( ( $pos[1] + $entity->height) < $anim->height * .75) {
- $pos[1]++;
- }
- }
-
- return @pos;
- }
- sub add_fishhook {
- my ($old_ent, $anim) = @_;
- my $hook_image =
- q{
- o
- ||
- ||
- / \ ||
- \__//
- `--'
- };
- my $point_image =
- q{
- .
-
- \
-
- };
- my $line_image = "|\n"x50 . " \n"x6;
- my $x = 10 + ( int(rand($anim->width() - 20)) );
- my $y = -4;
- my $point_x = $x + 1;
- my $point_y = $y + 2;
- $anim->new_entity(
- type => 'fishline',
- shape => $line_image,
- position => [ $x + 7, $y - 50, $depth{'water_line1'} ],
- auto_trans => 1,
- callback_args => undef,
- callback => \&fishhook_cb,
- );
- $anim->new_entity(
- type => 'fishhook',
- shape => $hook_image,
- trans_char => ' ',
- position => [ $x, $y, $depth{'water_line1'} ],
- auto_trans => 1,
- die_offscreen => 1,
- death_cb => sub { group_death(@_, 'teeth', 'fishline') },
- default_color => 'GREEN',
- callback_args => undef,
- callback => \&fishhook_cb,
- );
- $anim->new_entity(
- type => 'hook_point',
- shape => $point_image,
- position => [ $point_x, $point_y, $depth{'shark'}+1 ],
- depth => $depth{'fish_end'} - $depth{'fish_start'},
- callback_args => undef,
- physical => 1,
- default_color => 'GREEN',
- callback => \&fishhook_cb,
-
- );
- }
- sub add_ship {
- my ($old_ent, $anim) = @_;
- my @ship_image = (
- q{
- | | |
- )_) )_) )_)
- )___))___))___)\
- )____)____)_____)\\\
- _____|____|____|____\\\\\__
- \ /
- },
- q{
- | | |
- (_( (_( (_(
- /(___((___((___(
- //(_____(____(____(
- __///____|____|____|_____
- \ /
- });
- my @ship_mask = (
- q{
- y y y
- w
- ww
- yyyyyyyyyyyyyyyyyyyywwwyy
- y y
- },
- q{
- y y y
- w
- ww
- yywwwyyyyyyyyyyyyyyyyyyyy
- y y
- });
- my $dir = int(rand(2));
- my $x = -24;
- my $speed = 1;
- if($dir) {
- $speed *= -1;
- $x = $anim->width()-2;
- }
- $anim->new_entity(
- color => $ship_mask[$dir],
- shape => $ship_image[$dir],
- auto_trans => 1,
- position => [ $x, 0, $depth{'water_gap1'} ],
- default_color => 'WHITE',
- callback_args => [ $speed, 0, 0 ],
- die_offscreen => 1,
- death_cb => \&random_object,
- );
- }
- sub add_whale {
- my ($old_ent, $anim) = @_;
- my @whale_image = (
- q{
- .-----:
- .' `.
- ,????/ (o) \
- \`._/ ,__)
- },
- q{
- :-----.
- .' `.
- / (o) \????,
- (__, \_.'/
- });
- my @whale_mask = (
- q{
- C C
- CCCCCCC
- C C C
- BBBBBBB
- BB BB
- B B BWB B
- BBBBB BBBB
- },
- q{
- C C
- CCCCCCC
- C C C
- BBBBBBB
- BB BB
- B BWB B B
- BBBB BBBBB
- }
- );
- my @water_spout = (
- q{
- :
- },q{
- :
- :
- },q{
- . .
- -:-
- :
- },q{
- . .
- .-:-.
- :
- },q{
- . .
- '.-:-.`
- ' : '
- },q{
- .- -.
- ; : ;
- },q{
- ; ;
- });
- my $dir = int(rand(2));
- my $x;
- my $speed = 1;
- my $spout_align;
- my @whale_anim;
- my @whale_anim_mask;
- if($dir) {
- $spout_align = 1;
- $speed *= -1;
- $x = $anim->width()-2;
- } else {
- $spout_align = 11;
- $x = -18;
- }
-
- # no water spout
- for (1..5) {
- push(@whale_anim, "\n\n\n" . $whale_image[$dir]);
- push(@whale_anim_mask, $whale_mask[$dir]);
- }
- # animate water spout
- foreach my $spout_frame (@water_spout) {
- my $whale_frame = $whale_image[$dir];
- my $aligned_spout_frame;
- $aligned_spout_frame = join("\n" . ' 'x$spout_align, split("\n", $spout_frame));
- $whale_frame = $aligned_spout_frame . $whale_image[$dir];
- push(@whale_anim, $whale_frame);
- push(@whale_anim_mask, $whale_mask[$dir]);
- }
- $anim->new_entity(
- color => \@whale_anim_mask,
- shape => \@whale_anim,
- auto_trans => 1,
- position => [ $x, 0, $depth{'water_gap2'} ],
- default_color => 'WHITE',
- callback_args => [ $speed, 0, 0, 1 ],
- die_offscreen => 1,
- death_cb => \&random_object,
- );
- }
- sub add_monster {
- my ($old_ent, $anim) = @_;
- my @monster_image = (
- [
- q{
- ____
- __??????????????????????????????????????????/ o \
- / \????????_?????????????????????_???????/ ____ >
- _??????| __ |?????/ \????????_????????/ \????| |
- | \?????| || |????| |?????/ \?????| |???| |
- },q{
- ____
- __?????????/ o \
- _?????????????????????_???????/ \?????/ ____ >
- _???????/ \????????_????????/ \????| __ |???| |
- | \?????| |?????/ \?????| |???| || |???| |
- },q{
- ____
- __????????????????????/ o \
- _??????????????????????_???????/ \????????_???????/ ____ >
- | \??????????_????????/ \????| __ |?????/ \????| |
- \ \???????/ \?????| |???| || |????| |???| |
- },q{
- ____
- __???????????????????????????????/ o \
- _??????????_???????/ \????????_??????????????????/ ____ >
- | \???????/ \????| __ |?????/ \????????_??????| |
- \ \?????| |???| || |????| |?????/ \????| |
- }
- ],[
- q{
- ____
- / o \??????????????????????????????????????????__
- < ____ \???????_?????????????????????_????????/ \
- | |????/ \????????_????????/ \?????| __ |??????_
- | |???| |?????/ \?????| |????| || |?????/ |
- },q{
- ____
- / o \?????????__
- < ____ \?????/ \???????_?????????????????????_
- | |???| __ |????/ \????????_????????/ \???????_
- | |???| || |???| |?????/ \?????| |?????/ |
- },q{
- ____
- / o \????????????????????__
- < ____ \???????_????????/ \???????_??????????????????????_
- | |????/ \?????| __ |????/ \????????_??????????/ |
- | |???| |????| || |???| |?????/ \???????/ /
- },q{
- ____
- / o \???????????????????????????????__
- < ____ \??????????????????_????????/ \???????_??????????_
- | |??????_????????/ \?????| __ |????/ \???????/ |
- | |????/ \?????| |????| || |???| |?????/ /
- }
- ]);
- my @monster_mask = (
- q{
- W
- },q{
- W
- });
- my $dir = int(rand(2));
- my $x;
- my $speed = 2;
- if($dir) {
- $speed *= -1;
- $x = $anim->width()-2;
- } else {
- $x = -64
- }
- my @monster_anim_mask;
- for(1..4) { push(@monster_anim_mask, $monster_mask[$dir]); }
- $anim->new_entity(
- shape => $monster_image[$dir],
- auto_trans => 1,
- color => \@monster_anim_mask,
- position => [ $x, 2, $depth{'water_gap2'} ],
- callback_args => [ $speed, 0, 0, .25 ],
- death_cb => \&random_object,
- die_offscreen => 1,
- default_color => 'GREEN',
- );
- }
- sub add_big_fish {
- my ($old_ent, $anim) = @_;
- my @big_fish_image = (
- q{
- ______
- `""-. `````-----.....__
- `. . . `-.
- : . . `.
- , : . . _ :
- : `. : (@) `._
- `. `..' . =`-. .__)
- ; . = ~ : .-"
- .' .'`. . . =.-' `._ .'
- : .' : . .'
- ' .' . . . .-'
- .'____....----''.'=.'
- "" .'.'
- ''"'`
- },q{
- ______
- __.....-----''''' .-""'
- .-' . . .'
- .' . . :
- : _ . . : ,
- _.' (@) : .' :
- (__. .-'= . `..' .'
- "-. : ~ = . ;
- `. _.' `-.= . . .'`. `.
- `. . : `. :
- `-. . . . `. `
- `.=`.``----....____`.
- `.`. ""
- '`"``
- });
- my @big_fish_mask = (
- q{
- 111111
- 11111 11111111111111111
- 11 2 2 111
- 1 2 2 11
- 1 1 2 2 1 1
- 1 11 1 1W1 111
- 11 1111 2 1111 1111
- 1 2 1 1 1 111
- 11 1111 2 2 1111 111 11
- 1 11 1 2 11
- 1 11 2 2 2 111
- 111111111111111111111
- 11 1111
- 11111
- },q{
- 111111
- 11111111111111111 11111
- 111 2 2 11
- 11 2 2 1
- 1 1 2 2 1 1
- 111 1W1 1 11 1
- 1111 1111 2 1111 11
- 111 1 1 1 2 1
- 11 111 1111 2 2 1111 11
- 11 2 1 11 1
- 111 2 2 2 11 1
- 111111111111111111111
- 1111 11
- 11111
- });
- my $dir = int(rand(2));
- my $x;
- my $speed = 3;
- if($dir) {
- $x = $anim->width()-1;
- $speed *= -1;
- } else {
- $x = -34;
- }
- my $max_height = 9;
- my $min_height = $anim->height() - 15;
- my $y = int(rand($min_height - $max_height)) + $max_height;
- my $color_mask = rand_color($big_fish_mask[$dir]);
- $anim->new_entity(
- shape => $big_fish_image[$dir],
- auto_trans => 1,
- color => $color_mask,
- position => [ $x, $y, $depth{'shark'} ],
- callback_args => [ $speed, 0, 0 ],
- death_cb => \&random_object,
- die_offscreen => 1,
- default_color => 'YELLOW',
- );
- }
- sub add_ducks {
- my ($old_ent, $anim) = @_;
- my @duck_image = (
- [
- q{
- _??????????_??????????_
- ,____(')=??,____(')=??,____(')<
- \~~= ')????\~~= ')????\~~= ')
- },q{
- _??????????_??????????_
- ,____(')=??,____(')<??,____(')=
- \~~= ')????\~~= ')????\~~= ')
- },q{
- _??????????_??????????_
- ,____(')<??,____(')=??,____(')=
- \~~= ')????\~~= ')????\~~= ')
- }
- ],[
- q{
- _??????????_??????????_
- >(')____,??=(')____,??=(')____,
- (` =~~/????(` =~~/????(` =~~/
- },q{
- _??????????_??????????_
- =(')____,??>(')____,??=(')____,
- (` =~~/????(` =~~/????(` =~~/
- },q{
- _??????????_??????????_
- =(')____,??=(')____,??>(')____,
- (` =~~/????(` =~~/????(` =~~/
- }
- ]
- );
-
- my @duck_mask = (
- q{
- g g g
- wwwwwgcgy wwwwwgcgy wwwwwgcgy
- wwww Ww wwww Ww wwww Ww
- },q{
- g g g
- ygcgwwwww ygcgwwwww ygcgwwwww
- wW wwww wW wwww wW wwww
- });
- my $dir = int(rand(2));
- my $x;
- my $speed = 1;
- if($dir) {
- $speed *= -1;
- $x = $anim->width()-2;
- } else {
- $x = -30
- }
-
- $anim->new_entity(
- shape => $duck_image[$dir],
- auto_trans => 1,
- color => $duck_mask[$dir],
- position => [ $x, 5, $depth{'water_gap3'} ],
- callback_args => [ $speed, 0, 0, .25 ],
- death_cb => \&random_object,
- die_offscreen => 1,
- default_color => 'WHITE',
- );
- }
- sub add_dolphins {
- my ($old_ent, $anim) = @_;
- my @dolphin_image = (
- [
- q{
- ,
- __)\_
- (\_.-' a`-.
- (/~~````(/~^^`
- },q{
- ,
- (\__ __)\_
- (/~.'' a`-.
- ````\)~^^`
- }
- ],[
- q{
- ,
- _/(__
- .-'a `-._/)
- '^^~\)''''~~\)
- },q{
- ,
- _/(__ __/)
- .-'a ``.~\)
- '^^~(/''''
- }
- ]
- );
-
-
- my @dolphin_mask = (
- q{
- W
- },q{
- W
- });
- my $dir = int(rand(2));
-
- my $x;
- my $speed = 1;
- my $distance = 15; # how far apart the dolphins are
-
- # right to left
- if($dir) {
- $speed *= -1;
- $distance *= -1;
- $x = $anim->width()-2;
-
- # left to right
- } else {
- $x = -13
- }
-
- my $up = [$speed,-.5,0,.5];
- my $down = [$speed,.5,0,.5];
- my $glide = [$speed,0,0,.5];
-
- my @path;
-
- for(1..14) { push(@path, $up); }
- for(1..2) { push(@path, $glide); }
- for(1..14) { push(@path, $down); }
- for(1..6) { push(@path, $glide); }
-
- my $dolphin3 = $anim->new_entity(
- shape => $dolphin_image[$dir],
- auto_trans => 1,
- color => $dolphin_mask[$dir],
- position => [ $x - ($distance * 2), 8, $depth{'water_gap3'} ],
- callback_args => [ 0, [@path] ],
- death_cb => \&random_object,
- die_offscreen => 0,
- default_color => 'blue',
- );
-
- my $dolphin2 = $anim->new_entity(
- shape => $dolphin_image[$dir],
- auto_trans => 1,
- color => $dolphin_mask[$dir],
- position => [ $x - $distance, 2, $depth{'water_gap3'} ],
- callback_args => [ 12, [@path] ],
- die_offscreen => 0,
- default_color => 'BLUE',
- );
-
- my $dolphin1 = $anim->new_entity(
- shape => $dolphin_image[$dir],
- auto_trans => 1,
- color => $dolphin_mask[$dir],
- position => [ $x, 5, $depth{'water_gap3'} ],
- callback_args => [ 24, [@path] ],
- # have the lead dolphin tell the others to die offscreen, since they start offscreen
- death_cb => sub{ $dolphin2->die_offscreen(1); $dolphin3->die_offscreen(1) },
- die_offscreen => 1,
- default_color => 'CYAN',
- );
-
- }
- sub add_swan {
- my ($old_ent, $anim) = @_;
- my @swan_image = (
- [
- q{
- ___
- ,_ / _,\
- | \ \( \|
- | \_ \\\
- (_ \_) \
- (\_ ` \
- \ -=~ /
- }
- ],[
- q{
- ___
- /,_ \ _,
- |/ )/ / |
- // _/ |
- / ( / _)
- / ` _/)
- \ ~=- /
- }
- ]
- );
-
- my @swan_mask = (
- q{
- g
- yy
- },q{
- g
- yy
- });
- my $dir = int(rand(2));
- my $x;
- my $speed = 1;
- if($dir) {
- $speed *= -1;
- $x = $anim->width()-2;
- } else {
- $x = -10
- }
-
- $anim->new_entity(
- shape => $swan_image[$dir],
- auto_trans => 1,
- color => $swan_mask[$dir],
- position => [ $x, 1, $depth{'water_gap3'} ],
- callback_args => [ $speed, 0, 0, .25 ],
- death_cb => \&random_object,
- die_offscreen => 1,
- default_color => 'WHITE',
- );
- }
- sub init_random_objects {
- return (
- \&add_ship,
- \&add_whale,
- \&add_monster,
- \&add_big_fish,
- \&add_shark,
- \&add_fishhook,
- \&add_swan,
- \&add_ducks,
- \&add_dolphins,
- );
- }
- # add one of the random objects to the screen
- sub random_object {
- my ($dead_object, $anim) = @_;
- my $sub = int(rand(scalar(@random_objects)));
- $random_objects[$sub]->($dead_object, $anim);
- }
- sub dprint {
- open(D, ">>", "debug");
- print D @_, "\n";
- close(D);
- }
- sub sighandler {
- my ($sig) = @_;
- if($sig eq 'INT') { quit(); }
- elsif($sig eq 'WINCH') {
- # ignore SIGWINCH, only redraw when requested
- }
- else { quit("Exiting with SIG$sig"); }
- }
- sub quit {
- my ($mesg) = @_;
- print STDERR $mesg, "\n" if(defined($mesg));
- exit;
- }
- sub initialize {
- # this may be paranoid, but i don't want to leave
- # the user's terminal in a state that they might not
- # know how to fix if we die badly
- foreach my $sig (keys %SIG) {
- $SIG{$sig} = 'sighandler' unless(defined($SIG{$sig}));
- }
- }
- sub center {
- my ($width, $mesg) = @_;
- my $l = length($mesg);
- if($l < $width) {
- return ' 'x(int(($width - length($mesg))/2)) . $mesg;
- }
- elsif($l > $width) {
- return(substr($mesg, 0, ($width - ($l + 3))) . "...");
- }
- else {
- return $mesg;
- }
- }
- sub rand_color {
- my ($color_mask) = @_;
- my @colors = ('c','C','r','R','y','Y','b','B','g','G','m','M');
- foreach my $i (1..9) {
- my $color = $colors[int(rand($#colors))];
- $color_mask =~ s/$i/$color/gm;
- }
- return $color_mask;
- }
|