#!/usr/bin/perl
#Starfield in console
#
# Copyright (c) 2006, Rob Bloom
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the <organization> nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY ROB BLOOM ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ROB BLOOM BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#


  ## Console info
# Wait so each redraw is this far apart from the next.
#colors for ncurses
init_pair(1, COLOR_BLACK, COLOR_BLACK);

  ## Starfield info
# Number of stars to draw.
$numstars = 500;
# Min speed of stars.
$minspeed = .05;
# Max
$maxspeed = 2;

#Star origination point (1 = right/top, 0 = left/bottom, .5 = center)
$width_center = .5;
$height_center = .5;

# Probablility of superspeed  (rand % $prob == 0)
$prob = 25;
# Super speed
$superspeed = 5;


          #############################
          ### DON'T EDIT BELOW HERE ###
          #############################

use Time::HiRes qw (usleep gettimeofday tv_interval);
use Curses;

$buf = new Curses;
start_color;
noecho;
curs_set(0);

#constants
$width = $COLS;
$height = $LINES;
$PI = 3.14159265358979323846;
$r = ($width + $height)/4;

# Randomly place ~$numstars stars. Change around to be divisible by three
# (we want an even amount of stars)

#center
$cx = $width * $width_center;
$cy = $height * (1 - $height_center);

for ($i = 0; $i < $numstars ; $i++) {
    # Distance from center
    @stard[$i] = rand($r)/20;

    # Velocity
	if (int(rand($prob)) == 0) {
		$s = $superspeed;
	} else {
		$s = $maxspeed;
	}
    @starv[$i] = $minspeed + rand ($s - $minspeed);

    # Direction (angle)
    @stara[$i] = rand(2 * $PI);

}

# Okay, now we do a main program loop.

#bootstrap the clock :O
$last = [gettimeofday];
$t = 0; #Time of last frame

while (1) {
    
    ## Draw the stars
    

    #Clear the screen (and buffer), since everything is in motion anyway.
    $buf->clear;

    ## For loop through the stars, drawing and updating them.
    for ($i = 0; $i < $numstars; $i++) {
        #Which way does it point?
        $dy = sin($stara[$i]);
        $dx = cos($stara[$i]);
   
		#Where is it at now? (y is 2x tall so 1/2 as strong)
		$y = $cy + ( $dy * $stard[$i] )/2;
		$x = $cx + ( $dx * $stard[$i] );

        $m = $stard[$i] * ($starv[$i]/2) * $maxspeed;

        if ($m < $r / 3) {
			$buf->attrset(A_BOLD | COLOR_PAIR(1));
		} elsif ($m < $r * 2 / 3) {
			$buf->attrset(A_NORMAL);
		} else {
			$buf->attrset(A_BOLD);
		}
		if ($y - int $y < .5) {
			$buf->addch($y,$x, "`");
		} else {
			$buf->addch($y,$x, ".");
		}

		#Move the star now, by increasing distance, resetting if it's out.
		#Multiply by time of last frame. This'll make it run at all FPS
		$stard[$i] += $starv[$i]*$t*($stard[$i]+.1);
        if ($y > $height || $y < 0 || $x > $width || $x < 0) {
			$stard[$i] = 0;
			$stara[$i] = rand(2 * $PI);
			if (int(rand($prob)) == 0) {
				$s = $superspeed;
			} else {
				$s = $maxspeed;
			}
			$starv[$i] = $minspeed + rand($maxspeed - $minspeed);
		}
	}
        
    #Move cursor to bottomleft for consistency
    
    #Wait until it's time to print.
    usleep 5000; # Give the CPU/network a break, this is a screensaver
    $t = tv_interval($last);
    $last = [gettimeofday]; # Get this done as soon as possible.
	$buf->refresh;
}

sub moveto {
    my ($x, $y) = @_;
    
    #Round to nearest whole.
    $x = sprintf("%.0f", $x);
    $y = sprintf("%.0f", $y);
    
    $x = 0 if ($x < 0);
    $x = $width if ($x > $width);
    $y = 0 if ($y < 0);
    $y = $width if ($y > $height);

    return (chr(27) . '[' . ($y+1) . ';' . ($x+1) . 'H');
}
