mirror of https://git.envs.net/envs/ops.git
2288 lines
49 KiB
Perl
Executable File
2288 lines
49 KiB
Perl
Executable File
#!/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 original 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
|
|
#
|
|
# Claudio Matsuoka <cmatsuoka@gmail.com>
|
|
# improved marine biodiversity (backported from the Asciiquarium Live
|
|
# Wallaper for Android)
|
|
# https://market.android.com/details?id=org.helllabs.android.asciiquarium
|
|
#
|
|
# driechers: https://github.com/driechers
|
|
# contributed the yellow submarine and re-introductoin of elements by
|
|
# Kirk Baucom that apparently were lost
|
|
# (https://github.com/cmatsuoka/asciiquarium/pull/6,
|
|
# https://github.com/cmatsuoka/asciiquarium/pull/5)
|
|
#
|
|
# Carl Pilcher:
|
|
# ASCII art for the submarine https://ascii.co.uk/art/submarine
|
|
#
|
|
# robert1003: https://github.com/robert1003
|
|
# contributed the swordfish implementation
|
|
# (https://github.com/cmatsuoka/asciiquarium/pull/12)
|
|
#
|
|
# ctr: (?)
|
|
# ASCII art for the swordfish https://ascii.co.uk/art/fish
|
|
#
|
|
# polettix: https://github.com/polettix
|
|
# assembling contributions
|
|
# (https://github.com/polettix/asciiquarium)
|
|
#
|
|
# UndeadLeech: (?)
|
|
# background color and transparency patch
|
|
# (https://github.com/rwxrob/dot/blob/2c4b63f77dcb235be4e218cd8febc5533420ee54/scripts/fishies#L110)
|
|
#
|
|
# 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 Getopt::Long qw(GetOptions);
|
|
use strict;
|
|
use warnings;
|
|
|
|
my $version = "1.2";
|
|
my $new_fish = 1;
|
|
my $new_monster = 1;
|
|
|
|
my $classic = 0;
|
|
my $transparent = 0;
|
|
my $print_version = 0;
|
|
|
|
GetOptions(
|
|
'classic' => \$classic,
|
|
'transparent' => \$transparent,
|
|
'version' => \$print_version,
|
|
) or die "Try: $0 --classic --transparent\n";
|
|
|
|
if ($print_version) {
|
|
VERSION_MESSAGE();
|
|
}
|
|
|
|
if ($classic) {
|
|
$new_fish = 0;
|
|
$new_monster = 0;
|
|
}
|
|
|
|
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);
|
|
if ($transparent) {
|
|
use_default_colors();
|
|
my $cid = 1;
|
|
for my $f ('WHITE', 'RED', 'GREEN', 'BLUE', 'CYAN', 'MAGENTA', 'YELLOW', 'BLACK') {
|
|
init_pair($cid, eval "Curses::COLOR_$f", -1);
|
|
$cid++;
|
|
}
|
|
}
|
|
|
|
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 = lc(getch());
|
|
|
|
if ($in eq 'q') {quit();} # Exit
|
|
elsif ($in eq 'r') {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 => 'WHITE',
|
|
);
|
|
}
|
|
|
|
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 @parm = @_;
|
|
|
|
if ($new_fish) {
|
|
if (int(rand(12)) > 8) {
|
|
add_new_fish(@parm);
|
|
}
|
|
else {
|
|
add_old_fish(@parm);
|
|
}
|
|
}
|
|
else {
|
|
add_old_fish(@parm);
|
|
}
|
|
}
|
|
|
|
sub add_new_fish {
|
|
my ($old_fish, $anim) = @_;
|
|
my @fish_image = (
|
|
|
|
q{
|
|
\\
|
|
/ \\
|
|
>=_('>
|
|
\\_/
|
|
/
|
|
},
|
|
q{
|
|
1
|
|
1 1
|
|
663745
|
|
111
|
|
3
|
|
},
|
|
q{
|
|
/
|
|
/ \\
|
|
<')_=<
|
|
\\_/
|
|
\\
|
|
},
|
|
q{
|
|
2
|
|
111
|
|
547366
|
|
111
|
|
3
|
|
},
|
|
q{
|
|
,
|
|
\}\\
|
|
\\ .' `\\
|
|
\}\}< ( 6>
|
|
/ `, .'
|
|
\}/
|
|
'
|
|
},
|
|
q{
|
|
2
|
|
22
|
|
6 11 11
|
|
661 7 45
|
|
6 11 11
|
|
33
|
|
3
|
|
},
|
|
q{
|
|
,
|
|
/\{
|
|
/' `. /
|
|
<6 ) >\{\{
|
|
`. ,' \\
|
|
\\\{
|
|
`
|
|
},
|
|
q{
|
|
2
|
|
22
|
|
11 11 6
|
|
54 7 166
|
|
11 11 6
|
|
33
|
|
3
|
|
},
|
|
q{
|
|
\\'`.
|
|
) \\
|
|
(`.??????_.-`' ' '`-.
|
|
\\ `.??.` (o) \\_
|
|
> >< ((( (
|
|
/ .`??`._ /_| /'
|
|
(.`???????`-. _ _.-`
|
|
/__/'
|
|
|
|
},
|
|
q{
|
|
1111
|
|
1 1
|
|
111 11111 1 1111
|
|
1 11 11 141 11
|
|
1 11 777 5
|
|
1 11 111 333 11
|
|
111 111 1 1111
|
|
11111
|
|
|
|
},
|
|
q{
|
|
.'`/
|
|
/ (
|
|
.-'` ` `'-._??????.')
|
|
_/ (o) '.??.' /
|
|
) ))) >< <
|
|
`\\ |_\\ _.'??'. \\
|
|
'-._ _ .-'???????'.)
|
|
`\\__\\
|
|
},
|
|
q{
|
|
1111
|
|
1 1
|
|
1111 1 11111 111
|
|
11 141 11 11 1
|
|
5 777 11 1
|
|
11 333 111 11 1
|
|
1111 1 111 111
|
|
11111
|
|
},
|
|
q{
|
|
,--,_
|
|
__ _\\.---'-.
|
|
\\ '.-" // o\\
|
|
/_.'-._ \\\\ /
|
|
`"--(/"`
|
|
},
|
|
q{
|
|
22222
|
|
66 121111211
|
|
6 6111 77 41
|
|
6661111 77 1
|
|
11113311
|
|
},
|
|
q{
|
|
_,--,
|
|
.-'---./_ __
|
|
/o \\\\ "-.' /
|
|
\\ // _.-'._\\
|
|
`"\\)--"`
|
|
},
|
|
q{
|
|
22222
|
|
112111121 66
|
|
14 77 1116 6
|
|
1 77 1111666
|
|
11331111
|
|
},
|
|
);
|
|
|
|
add_fish_entity($anim, @fish_image);
|
|
}
|
|
|
|
sub add_old_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
|
|
},
|
|
);
|
|
|
|
add_fish_entity($anim, @fish_image);
|
|
}
|
|
|
|
sub add_fish_entity {
|
|
my $anim = shift;
|
|
my @fish_image = @_;
|
|
|
|
# 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" x 50 . " \n" x 6;
|
|
|
|
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 @parm = @_;
|
|
|
|
if ($new_monster) {
|
|
add_new_monster(@parm);
|
|
}
|
|
else {
|
|
add_old_monster(@parm);
|
|
}
|
|
}
|
|
|
|
sub add_new_monster {
|
|
my ($old_ent, $anim) = @_;
|
|
my @monster_image = (
|
|
[
|
|
"
|
|
_???_?????????????????????_???_???????_a_a
|
|
_{.`=`.}_??????_???_??????_{.`=`.}_????{/ ''\\_
|
|
_????{.' _ '.}????{.`'`.}????{.' _ '.}??{| ._oo)
|
|
{ \\??{/ .'?'. \\}??{/ .-. \\}??{/ .'?'. \\}?{/ |
|
|
",
|
|
"
|
|
_???_????????????????????_a_a
|
|
_??????_???_??????_{.`=`.}_??????_???_??????{/ ''\\_
|
|
{ \\????{.`'`.}????{.' _ '.}????{.`'`.}????{| ._oo)
|
|
\\ \\??{/ .-. \\}??{/ .'?'. \\}??{/ .-. \\}???{/ |
|
|
"
|
|
], [
|
|
"
|
|
a_a_???????_???_?????????????????????_???_
|
|
_/'' \\}????_{.`=`.}_??????_???_??????_{.`=`.}_
|
|
(oo_. |}??{.' _ '.}????{.`'`.}????{.' _ '.}????_
|
|
| \\}?{/ .'?'. \\}??{/ .-. \\}??{/ .'?'. \\}??/ }
|
|
",
|
|
"
|
|
a_a_????????????????????_ _
|
|
_/'' \\}??????_???_??????_{.`=`.}_??????_???_??????_
|
|
(oo_. |}????{.`'`.}????{.' _ '.}????{.`'`.}????/ }
|
|
| \\}???{/ .-. \\}??{/ .'?'. \\}??{/ .-. \\}??/ /
|
|
"
|
|
]);
|
|
|
|
my @monster_mask = (
|
|
q{ W W
|
|
|
|
|
|
|
|
}, q{
|
|
W W
|
|
|
|
|
|
|
|
});
|
|
my $dir = int(rand(2));
|
|
my $x;
|
|
my $speed = 2;
|
|
if ($dir) {
|
|
$speed *= -1;
|
|
$x = $anim->width() - 2;
|
|
}
|
|
else {
|
|
$x = -54
|
|
}
|
|
my @monster_anim_mask;
|
|
for (1 .. 2) {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_old_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 @parm = @_;
|
|
|
|
if ($new_fish) {
|
|
if (int(rand(3)) > 1) {
|
|
add_big_fish_2(@parm);
|
|
}
|
|
else {
|
|
add_big_fish_1(@parm);
|
|
}
|
|
}
|
|
else {
|
|
add_big_fish_1(@parm);
|
|
}
|
|
}
|
|
|
|
sub add_big_fish_1 {
|
|
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_big_fish_2 {
|
|
my ($old_ent, $anim) = @_;
|
|
|
|
my @big_fish_image = (
|
|
q{
|
|
_ _ _
|
|
.='\\ \\ \\`"=,
|
|
.'\\ \\ \\ \\ \\ \\ \\
|
|
\\'=._?????/ \\ \\ \\_\\_\\_\\_\\_\\
|
|
\\'=._'.??/\\ \\,-"`- _ - _ - '-.
|
|
\\`=._\\|'.\\/- _ - _ - _ - _- \\
|
|
;"= ._\\=./_ -_ -_ \{`"=_ @ \\
|
|
;="_-_=- _ - _ - \{"=_"- \\
|
|
;_=_--_., \{_.=' .-/
|
|
;.="` / ';\\ _. _.-`
|
|
/_.='/ \\/ /;._ _ _\{.-;`/"`
|
|
/._=_.'???'/ / / / /\{.= /
|
|
/.=' ??????`'./_/_.=`\{_/
|
|
}, q{
|
|
_ _ _
|
|
,="`/ / /'=.
|
|
/ / / / / / /'.
|
|
/_/_/_/_/_/ / / \\?????_.='/
|
|
.-' - _ - _ -`"-,/ /\\??.'_.='/
|
|
/ -_ - _ - _ - _ -\\/.'|/_.=`/
|
|
/ @ _="`\} _- _- _\\.=/_. =";
|
|
/ -"_="\} - _ - _ -=_-_"=;
|
|
\\-. '=._\} ,._--_=_;
|
|
`-._ ._ /;' \\ `"=.;
|
|
`"\\`;-.\}_ _ _.;\\ \\/ \\'=._\\
|
|
\\ =.\}\\ \\ \\ \\ \\'???'._=_.\\
|
|
\\_\}`=._\\_\\.'`???????'=.\\
|
|
});
|
|
|
|
my @big_fish_mask = (
|
|
q{
|
|
1 1 1
|
|
1111 1 11111
|
|
111 1 1 1 1 1 1
|
|
11111 1 1 1 11111111111
|
|
1111111 11 111112 2 2 2 2 111
|
|
111111111112 2 2 2 2 2 2 22 1
|
|
111 1111 12 22 22 11111 W 1
|
|
11111112 2 2 2 2 111111 1
|
|
111111111 11111 111
|
|
11111 11111 11 1111
|
|
111111 11 1111 1 111111111
|
|
1111111 11 1 1 1 1111 1
|
|
1111 1111111111111
|
|
}, q{
|
|
1 1 1
|
|
11111 1 1111
|
|
1 1 1 1 1 1 111
|
|
11111111111 1 1 1 11111
|
|
111 2 2 2 2 211111 11 1111111
|
|
1 22 2 2 2 2 2 2 211111111111
|
|
1 W 11111 22 22 2111111 111
|
|
1 111111 2 2 2 2 21111111
|
|
111 11111 111111111
|
|
1111 11 111 1 11111
|
|
111111111 1 1111 11 111111
|
|
1 1111 1 1 1 11 1111111
|
|
1111111111111 1111
|
|
});
|
|
|
|
my $dir = int(rand(2));
|
|
my $x;
|
|
my $speed = 2.5;
|
|
if ($dir) {
|
|
$x = $anim->width() - 1;
|
|
$speed *= -1;
|
|
}
|
|
else {
|
|
$x = -33;
|
|
}
|
|
my $max_height = 9;
|
|
my $min_height = $anim->height() - 14;
|
|
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 submarine_callback {
|
|
my ($entity, $anim) = @_;
|
|
|
|
my @delta = $entity->callback_args();
|
|
my @pos = $entity->position();
|
|
|
|
my $x = $pos[0];
|
|
my $y = $pos[1];
|
|
my $z = $pos[2];
|
|
my $frame = $delta[0][3];
|
|
my $delay = $delta[0][4];
|
|
my @frame_delay = @{[ 1, 4, 4, 9, 9, 9, 4, 4, 1 ]};
|
|
|
|
my $maxFrame = scalar(@frame_delay);
|
|
|
|
# Move if not in center or skipping over the center
|
|
if (not (($x < $anim->width() / 2 - 20 and $x + $delta[0][0] > $anim->width() / 2 - 20) or
|
|
($x > $anim->width() / 2 - 20 and $x + $delta[0][0] < $anim->width() / 2 - 20))
|
|
and $x != $anim->width() / 2 - 20) {
|
|
$x += $delta[0][0];
|
|
}
|
|
else {
|
|
if ($frame < $maxFrame - 1) {
|
|
if ($delay < $frame_delay[$frame]) {
|
|
$delay += 1;
|
|
}
|
|
else {
|
|
$delay = 0;
|
|
$frame += 1;
|
|
}
|
|
}
|
|
else {
|
|
$x += $delta[0][0];
|
|
}
|
|
}
|
|
|
|
my @args = [ $delta[0][0], $delta[0][1], $delta[0][2], $frame, $delay ];
|
|
$entity->callback_args(@args);
|
|
return ($x, $y, $z, $frame);
|
|
}
|
|
|
|
sub add_submarine {
|
|
my ($old_ent, $anim) = @_;
|
|
my @submarine_image = (
|
|
[
|
|
q{
|
|
|
|
|
|
|
|
__
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
|
|
|
|
__
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
|
|
__
|
|
|
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
__
|
|
|
|
|
|
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
__
|
|
|
|
|
|
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
__
|
|
|
|
|
|
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
|
|
__
|
|
|
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
|
|
|
|
__
|
|
|
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
, q{
|
|
|
|
|
|
|
|
__
|
|
_|_
|
|
/ |
|
|
_ ? _______________/____|_______________
|
|
( )?/ /
|
|
(=< O O O /
|
|
(_)?\_________________________________/
|
|
}
|
|
], [
|
|
q{
|
|
|
|
|
|
|
|
__
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
|
|
|
|
__
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
|
|
__
|
|
|
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
__
|
|
|
|
|
|
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
__
|
|
|
|
|
|
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
__
|
|
|
|
|
|
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
|
|
__
|
|
|
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
|
|
|
|
__
|
|
|
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
, q{
|
|
|
|
|
|
|
|
__
|
|
_|_
|
|
| \
|
|
______________|____\_______________ ? _
|
|
\ \?( )
|
|
\ O O O >=)
|
|
\________________________________/?(_)
|
|
}
|
|
]
|
|
);
|
|
|
|
my @submarine_mask = (
|
|
q{
|
|
www
|
|
ww
|
|
ww
|
|
ww
|
|
w
|
|
|
|
w
|
|
w w
|
|
www w w w
|
|
www
|
|
}, q{
|
|
www
|
|
ww
|
|
ww
|
|
ww
|
|
w
|
|
|
|
w
|
|
w w
|
|
w w w www
|
|
www
|
|
});
|
|
|
|
my $dir = int(rand(2));
|
|
my $x;
|
|
my $speed = 1;
|
|
if ($dir) {
|
|
$speed *= -1;
|
|
$x = $anim->width() - 2;
|
|
}
|
|
else {
|
|
$x = -40
|
|
}
|
|
|
|
$anim->new_entity(
|
|
shape => $submarine_image[$dir],
|
|
auto_trans => 1,
|
|
color => $submarine_mask[$dir],
|
|
position => [ $x, 6, $depth{'water_gap3'} ],
|
|
callback_args => [ $speed, 0, 0, 0, 0 ],
|
|
callback => \&submarine_callback,
|
|
death_cb => \&random_object,
|
|
die_offscreen => 1,
|
|
default_color => 'YELLOW',
|
|
);
|
|
}
|
|
|
|
sub add_sword_fish {
|
|
my ($old_end, $anim) = @_;
|
|
|
|
my @sword_fish_image = (
|
|
q{
|
|
)",
|
|
_ /\\\\\\\\
|
|
", _____,,,,,--\\\\\\\\\\\\)
|
|
\\\\ ,,;_______\\\\\\\\\\----""";_
|
|
)\\_.-'`"""""......-------------,o";__________
|
|
==.-.,____ """;;;;,__._.=='""
|
|
)\\ " "\\""---;...,______:.-"
|
|
// ")_.-` _.-'
|
|
_.' _.-'
|
|
},
|
|
q{
|
|
,"(
|
|
////\ _
|
|
(//////--,,,,,_____ ,"
|
|
_;"""----/////_______;,, //
|
|
__________;"o,-------------......"""""`'-._/(
|
|
""'==._.__,;;;;""" ____,.-.==
|
|
"-.:______,...;---""/" " \\(
|
|
'-._ `-._(" \\\\
|
|
'-._ '._
|
|
});
|
|
|
|
my @sword_fish_mask = (
|
|
q{
|
|
222
|
|
6 22222
|
|
66 2222222222222222222
|
|
66 111111111111111111111111
|
|
666111111111111111111111111111144455555555555
|
|
6661111111 111777711111155555
|
|
66 1 11111111111111111111111
|
|
66 3333333 3333
|
|
666 3333
|
|
},
|
|
q{
|
|
222
|
|
22222 6
|
|
2222222222222222222 66
|
|
111111111111111111111111 66
|
|
555555555554441111111111111111111111111111666
|
|
555551111117777111 1111111666
|
|
11111111111111111111111 1 66
|
|
3333 3333333 66
|
|
3333 666
|
|
});
|
|
|
|
my $dir = int(rand(2));
|
|
my $x;
|
|
my $speed = 3.5;
|
|
if ($dir) {
|
|
$x = $anim->width() - 1;
|
|
$speed *= -1;
|
|
}
|
|
else {
|
|
$x = -33;
|
|
}
|
|
my $max_height = 9;
|
|
my $min_height = $anim->height() - 14;
|
|
my $y = int(rand($min_height - $max_height)) + $max_height;
|
|
my $color_mask = rand_color($sword_fish_mask[$dir]);
|
|
$anim->new_entity(
|
|
shape => $sword_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 init_random_objects {
|
|
return (
|
|
\&add_ship,
|
|
\&add_whale,
|
|
\&add_monster,
|
|
\&add_big_fish,
|
|
\&add_shark,
|
|
\&add_fishhook,
|
|
\&add_swan,
|
|
\&add_ducks,
|
|
\&add_dolphins,
|
|
\&add_submarine,
|
|
\&add_sword_fish,
|
|
);
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
sub VERSION_MESSAGE {
|
|
print "asciiquarium $version\n";
|
|
exit;
|
|
}
|