#! /usr/bin/ruby

#==========================================================
# galint.rb - Juego de naves usando la biblioteca Gosu
#----------------------------------------------------------
# Tomando como base Tutorial.rb, script que forma parte de 
# los ejemplos que acompañan a la biblioteca Gosu. 
# Proyecto en: http://code.google.com/p/gosu/
#==========================================================
# FJA - neocipres@gmail.com 	Diciembre de 2010
#==========================================================

require 'rubygems'
require 'gosu'

XMAX = 800
YMAX = 600
DATOS = '/home/paco/prgdgnu/ruby/gosu/galint/rec/'

module ZOrder
  Background, Star, Foton, Player,  UI = *0..4
end

class Nube
 attr_reader :x, :y, :t
  
 def initialize(window, x, y, vx, vy, t)
    @imagen = Gosu::Image.new(window, "#{DATOS}nube.png", false)
    @x, @y = x, y
    @vx, @vy = vx, vy
    @t = t
 end
 
  def move
  	@x += @vx  
    @y += @vy
    @x %= XMAX        
    @y %= YMAX
  end
  
 def draw
    @imagen.draw_rot(@x, @y, ZOrder::Star, 0)
 end
 
end

class Explosion
 attr_reader :t
  
 def initialize(window, x, y, t)
    @imagen = Gosu::Image.new(window, "#{DATOS}exp.png", false)
    @x = x
    @y = y
    @t = t
 end
 
 def draw
    @imagen.draw_rot(@x, @y, ZOrder::Foton, 0)
 end
 
end

class Star
  attr_reader :x, :y, :vel_x, :vel_y
  
  def initialize(animation, vx, vy)
    @animation = animation
    @color = Gosu::Color.new(0xff000000)
    @color.red = rand(255 - 40) + 40
    @color.green = rand(255 - 40) + 40
    @color.blue = rand(255 - 40) + 40
    @x = rand * XMAX
    @y = rand * YMAX
    @vel_x, @vel_y = vx, vy
  end

  def draw  
    img = @animation[Gosu::milliseconds / 100 % @animation.size]
    img.draw(@x - img.width / 2.0, @y - img.height / 2.0,
        ZOrder::Star, 1, 1, @color, :additive)
  end
  
  def move
  	@x += @vel_x  
    @y += @vel_y
  end
  
  def Star.destruir(estrellas)     # Método de clase
 	estrellas.reject! {|e| e.x < 0 or e.x > XMAX or e.y < 0 or e.y > YMAX}      
  end
  
  def exp_stars(foton)
    if Gosu::distance(@x, @y, foton.x, foton.y) < 5 then
      	true
    else
    	false
    end
  end
  
end

class Foton
 attr_reader :x, :y
 
 def initialize(window, vx, vy)
    @imagen = Gosu::Image.new(window, "#{DATOS}foton.png", false)
    @x = @y = @angle = 0.0
    @vel_x, @vel_y = vx, vy
 end
 
 def pos(x,y)
 	@x, @y = x, y
 end
 
 def move
    @x += @vel_x  
    @y += @vel_y  
 end 
 
 def Foton.destruir(fotones)     # Método de clase
 	fotones.reject! {|f| f.x < 0 or f.x > XMAX or f.y < 0 or f.y > YMAX}      
 end
 
 def draw
    @imagen.draw_rot(@x, @y, ZOrder::Foton, @angle)
 end
 
end

class Player
  attr_reader :score, :vel_x, :vel_y, :x, :y, :angle, :fin, :tf

  def initialize(window)
    @imagen = Gosu::Image.new(window, "#{DATOS}nave.bmp", false)
    @imgexp = Gosu::Image.new(window, "#{DATOS}exp.png", false)
    @recs = Gosu::Sample.new(window, "#{DATOS}slurp.wav") 
    @x = @y = @vel_x = @vel_y = @angle = 0.0
    @score = 0
    @fin = 0
  end
  
  def puntos(p)
  	@score += p
  end

  def pos(x, y)
    @x, @y = x, y
  end
  
  def turn_left
    @angle -= 2.5
  end
  
  def turn_right
    @angle += 2.5
  end
  
  def accelerate
    @vel_x += Gosu::offset_x(@angle, 0.3)
    @vel_y += Gosu::offset_y(@angle, 0.3)
  end
  
  def move(vx,vy)
    @x += vx  
    @y += vy  
    
    @x %= XMAX         # Permite que la nave no se escape de la ventana
    @y %= YMAX
  end
    
  def stop
  	@vel_x *= 0.65 
  	@vel_y *= 0.65
  end
  
  def colector_nubes(nubes)
  	nubes.reject! do |n|
    	if Gosu::distance(@x, @y, n.x, n.y) < 15 then
    		@score += 20
    		@recs.play
      		true
    	else
    		false
    	end
    end
  end

  def draw
    @imagen.draw_rot(@x, @y, ZOrder::Player, @angle)
  end
  
  def kill(estrellas)
  	estrellas.each do |e|
  		if Gosu::distance(@x, @y, e.x, e.y) < 15 then
  			@fin = 1
  			@tf = Time.now
  			@imagen = @imgexp
  		end
  	end  	
  end  
  
end

class GameWindow < Gosu::Window
  def initialize
    super(XMAX, YMAX, false)
    self.caption = "Interceptor Galáctico"
   
    # Imágenes y sonidos
    @imagen_fondo = Gosu::Image.new(self, "#{DATOS}fondo.png", true)
    @laser = Gosu::Sample.new(self, "#{DATOS}laser.wav")
    @exps = Gosu::Sample.new(self, "#{DATOS}exp.wav")
    @ti = Time.now
       
    # Contenedores de sprites
    @fotones = []
    @explosiones = []
    @nubes = []
    @star_anim = Gosu::Image::load_tiles(self, "#{DATOS}star.png", 25, 25, false)
    @stars = Array.new
    
    @n, @nx, @ny, @vx, @vy = 0
    
    @player = Player.new(self)
    @player.pos(XMAX/2, YMAX/2)
    
    @font = Gosu::Font.new(self, Gosu::default_font_name, 20)
    @ffont = Gosu::Font.new(self, Gosu::default_font_name, 48)
  end

  def update
  	if @player.fin == 0 then
  		if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft then
      		@player.turn_left
    	end
    	if button_down? Gosu::KbRight or button_down? Gosu::GpRight then
      		@player.turn_right
    	end
    	if button_down? Gosu::KbUp or button_down? Gosu::GpButton0 then
      		@player.accelerate
    	end
    end
    
    # Movimiento de los sprites
    @player.move(@player.vel_x,@player.vel_y) if @player.fin == 0
    if !@fotones.empty? then
    	@fotones.each {|f| f.move} 
    	Foton.destruir(@fotones) 
    end
    @nubes.each {|n| n.move} if !@nubes.empty? 
    if !@stars.empty? then
    	@stars.each {|s| s.move}
    	Star.destruir(@stars) 
    	
    	# Controla la colisión fotón - estrella 
    	@fotones.each do |f|
    		@stars.reject! do |s|
    				exp = s.exp_stars(f) 
    				if exp then
    					f.pos(-5,-5)
    					t = Time.now
    					@explosion = Explosion.new(self, s.x, s.y, t)
    					@explosiones << @explosion
    					@exps.play
    					@nube = Nube.new(self, s.x, s.y, s.vel_x, s.vel_y, t)
    					@nubes << @nube
    					if s.vel_x == 0 && s.vel_y == 0 then
    						@player.puntos(5)
    					else
    						@player.puntos(10)
    					end	
    				end
    		end
    	end
    end
    # Controla la recogida de nubes
    @player.colector_nubes(@nubes)
    
    # Controla la duración de la explosión y de la nube
    @explosiones.reject!{|e| Time.now - e.t > 1} 
    @nubes.reject!{|n| Time.now - n.t > 9}  
    
    # Controla el número y la velocidad de las estrellas
    @n = rand(299)
    @nx = rand(3)
    @ny = rand(3)
    case @nx
    	when 0
    		@vx = 0
    	when 1
    		@vx = 1
    	when 2
    		@vx = -1
    end
    case @ny
    	when 0
    		@vy = 0
    	when 1
    		@vy = 1
    	when 2
    		@vy = -1
    end		
    
    # Controla la dificultad del juego
    @tiempo = (Time.now - @ti).to_i
    if @tiempo > 180 then @gd = 10
    elsif @tiempo > 60 && @tiempo < 180 then @gd = 7 
    else @gd = 4
    end
    if @n < @gd and @stars.size < 25 then
      @stars.push(Star.new(@star_anim, @vx, @vy))
    end
    
    # Controla el fin del juego
    @player.kill(@stars) if !@stars.empty? && @player.fin == 0
    if @player.fin == 1 then
    	@exps.play if (Time.now - @player.tf) < 2 
    	close if (Time.now - @player.tf) > 9
    end  
  end

  def draw
  	@imagen_fondo.draw(0,0,ZOrder::Background)
  	@player.draw
  	@fotones.each {|f| f.draw} 
  	@stars.each {|s| s.draw} 
  	@explosiones.each {|e| e.draw} 
  	@nubes.each {|n| n.draw} 
  	
  	# Información en pantalla
  	@font.draw("Estrellas: #{@stars.size}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xff00ffff)
  	@font.draw("Puntos: #{@player.score}", 120, 10, ZOrder::UI, 1.0, 1.0, 0xff00ffff)
  	@font.draw("Tiempo: #{@tiempo}", XMAX - 150, 10, ZOrder::UI, 1.0, 1.0, 0xff00ffff)
  	if @player.fin == 1 then
  		@ffont.draw("Nave destruida...   Game Over !!!", 70, YMAX/2, ZOrder::UI, 1.0, 1.0, 0xffff0000) 	 
  	end
  	
  end
  
  def button_down(id)
  	case id
  		when Gosu::KbEscape
  			close
  		when Gosu::KbDown
  			@player.stop
  		when Gosu::KbSpace
  			if @player.fin == 0 then
  				@foton = Foton.new( self, 
  								Gosu::offset_x(@player.angle, 5),
  								Gosu::offset_y(@player.angle, 5))
  				@foton.pos(@player.x, @player.y)
  				@fotones << @foton
  				@laser.play
  			end  	
  	end
   end
end

window = GameWindow.new
window.show