import pygame
import spyral

import random;
import time;
import os;

TICKS_PER_SECOND = 60

colors = {}
images = {}
geom = {}
fonts = {}
strings = {}
sounds= {}

UP, DOWN, LEFT, RIGHT= 0,1,2,3;

class Score(spyral.sprite.Sprite):
	def __init__(self):
		spyral.sprite.Sprite.__init__(self)
		self.score = 0
		self.render()
	
	def increaseScore(self):
		self.score+= 1;
		self.render()
		
	def render(self):
		self.image = fonts['score'].render("%d" % (self.score,),
										   True,
										   colors['score'])
		self.rect.midtop = (geom['width']/2, geom['score_in_height']);

class Gunpowder(spyral.sprite.Sprite):
	def __init__(self):
		spyral.sprite.Sprite.__init__(self)
		self.image= images['gunpowder'];
		self.warp();
		
	def warp(self):
		randX= random.randint(0,geom['tiles_across']-1);
		randY= random.randint(0,geom['tiles_down']-1);
		self.pos= (randX * geom['tile_width'], randY * geom['tile_height']);
	
	def collides(self, creeper):
		return self.pos == creeper.pos;

class Explosion(spyral.sprite.Sprite):
	def __init__(self, position):
		spyral.sprite.Sprite.__init__(self)
		self.imageList= images['explosion'];
		self.subimage= 0;
		self.imageSpeed= 1;
		self.imageDelay= 0;
		self.updateSubimage();
		self.rect.center= position;
		self.rect= self.rect.move(random.randint(-5, 5),random.randint(-5, 5));
	
	def updateSubimage(self):
		self.image= self.imageList[self.subimage];
	
	def nextSubimage(self):
		if self.subimage < len(self.imageList)-1:
			self.subimage+= 1;
		else:
			self.kill();
		self.imageDelay= 0;
	
	def animationFinished(self):
		return (self.imageDelay >= 1);
	
	def delayAnimation(self):
		self.imageDelay+= self.imageSpeed;
	
	def update(self, camera):
		if self.animationFinished():
			self.nextSubimage();
			self.updateSubimage();
		else:
			self.delayAnimation();

class Creeper(spyral.sprite.Sprite):
	def __init__(self):
		spyral.sprite.Sprite.__init__(self)
		self.speed = 2
		self.subimage= 0;
		self.imageSpeed= .25;
		self.imageDelay= 0;
		self.follower= None;
		self.setDirection(None);
	
	def setDirection(self, direction):
		if direction == UP:
			self.imageList = images['creeper-up'];
		elif direction == DOWN:
			self.imageList = images['creeper-down'];
		elif direction == LEFT:
			self.imageList = images['creeper-left'];
		elif direction == RIGHT:
			self.imageList = images['creeper-right'];
		elif direction == None:
			self.imageList = images['creeper-down'];
		self.updateSubimage();
		self.direction= direction;
	
	def updateSubimage(self):
		self.image= self.imageList[self.subimage];
	
	def nextSubimage(self):
		if self.subimage < len(self.imageList)-1:
			self.subimage+= 1;
		else:
			self.subimage= 0;
		self.imageDelay= 0;
	
	def animationFinished(self):
		return (self.imageDelay >= 1);
	
	def delayAnimation(self):
		self.imageDelay+= self.imageSpeed;
	
	def atFocalPoint(self):
		tileX= self.pos[0] % geom['tile_width'];
		tileY= self.pos[1] % geom['tile_height'];
		return (tileX == 0 and tileY == 0);
	
	def moveForward(self):
		if self.direction == UP:
			movePos= (0,-self.speed);
		elif self.direction == DOWN:
			movePos= (0,self.speed);
		elif self.direction == LEFT:
			movePos= (-self.speed,0);
		elif self.direction == RIGHT:
			movePos= (self.speed,0);
		else:
			movePos= (0,0);
			self.subimage= 0;
		self.pos= spyral.point.add(self.pos, movePos);
	
	def hittingWall(self):
		if self.pos[0] < 0:
			return True;
		if self.pos[0] > geom['width']-geom['tile_width']:
			return True;
		if self.pos[1] < 0:
			return True;
		if self.pos[1] > geom['height']-geom['tile_height']:
			return True;
		else:
			return False;
	def wrap(self):
		wrapWidth= geom['tile_width']+geom['width'];
		wrapHeight= geom['tile_height']+geom['height'];
		if self.pos[0] < -geom['tile_width']:
			self.pos= spyral.point.add(self.pos, (wrapWidth,0));
		if self.pos[0] > geom['width']:
			self.pos= spyral.point.sub(self.pos, (wrapWidth,0));
		if self.pos[1] < -geom['tile_height']:
			self.pos= spyral.point.add(self.pos, (0,wrapHeight));
		if self.pos[1] > geom['height']:
			self.pos= spyral.point.sub(self.pos, (0,wrapHeight));
	
	def explode(self):
		if self.alive():
			spriteGroup= self.groups()[0];
			spriteGroup.add(Explosion(self.rect.center));
			if self.follower is not None:
				self.follower.explode();
			self.kill();
		
	def update(self, camera):
		if self.animationFinished():
			self.nextSubimage();
			self.updateSubimage();
		else:
			self.delayAnimation();
	
class CreeperFollower(Creeper):
	def __init__(self):
		Creeper.__init__(self);
		self.target= None;
		self.sleeping= False;
		self.safe= True;
	
	def face(self, target):
		if self.target is not None:
			diff= spyral.point.sub(target, self.pos);
			angle= int(spyral.point.angle_d(diff)) % 360;
			angle= int(round(float(angle)/90) * 90);
			if angle == 0:
				self.setDirection(RIGHT);
			elif angle == 90:
				self.setDirection(DOWN);
			elif angle == 180:
				self.setDirection(LEFT);
			elif angle == 270:
				self.setDirection(UP);
			else:
				self.setDirection(None);
	
	def signalFollower(self):
		if self.follower is not None:
			self.follower.target= self.pos;
			self.follower.signalFollower();
			self.safe= False;
		
	def update(self, camera):
		Creeper.update(self, camera);
		self.face(self.target);
		self.moveForward();
		
		if self.target == self.pos:
			self.direction= None;

class CreeperLeader(Creeper):
	def __init__(self):
		Creeper.__init__(self);
		self.queuedDirection= None;
	def hittingCreepers(self, creepers):
		hittingAnything= False;
		this= spyral.point.snap(geom['tile_width'], geom['tile_height'], self.pos);
		for aCreeper in creepers:
			if not aCreeper.safe:
				other= spyral.point.snap(geom['tile_width'], geom['tile_height'], aCreeper.pos);
				hittingThis= (this == other);
				hittingAnything= hittingAnything or hittingThis;
		return hittingAnything;
	def signalFollower(self):
		if self.follower is not None:
			#time.sleep(1);
			if self.follower.sleeping:
				self.follower.sleeping= False;
			else:
				self.follower.target= self.pos;
				self.follower.signalFollower();
	def update(self, camera):
		Creeper.update(self, camera);
		if self.atFocalPoint():
			self.setDirection(self.queuedDirection);
			self.signalFollower();
		self.moveForward();

class Sssneak(spyral.scene.Scene):
	def __init__(self):
		spyral.scene.Scene.__init__(self)
		self.camera = spyral.director.get_camera()
		self.group = spyral.sprite.Group(self.camera)
		bg= images['background'];
		self.camera.set_background(bg)
		
		self.bossCreeper = CreeperLeader()
		self.creepers= [self.bossCreeper];
		center = (geom['width']/2, geom['height']/2)
		center= spyral.point.snap(geom['tile_width'], geom['tile_height'], center);
		self.bossCreeper.rect.topleft= center;
		self.gunpowder = Gunpowder()
		self.score = Score()
		self.group.add(self.bossCreeper, self.gunpowder, self.score)
		
		self.game_over1 = spyral.sprite.Sprite()
		self.game_over1.image = images['game_over1']
		self.game_over1.rect.center = self.camera.get_rect().center
		
		self.game_over2 = spyral.sprite.Sprite()
		self.game_over2.image = images['game_over2']
		self.game_over2.rect.top = self.game_over1.rect.bottom + 10
		self.game_over2.rect.centerx = self.camera.get_rect().centerx
		
		self.isGameover= False;
				
	def render(self):
		"""
		The render function should call .draw() on the scene's group(s) and
		camera(s). Unless your game logic should be bound by framerate,
		logic should go elsewhere.
		"""
		self.group.draw()
		self.camera.draw()
	
	def gameover(self):
		self.group.add(self.game_over1,self.game_over2);
		sounds['explode'].play();
		self.isGameover= True;
		
	def update(self, tick):
		for event in pygame.event.get([pygame.KEYUP, pygame.KEYDOWN, pygame.QUIT]):
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_SPACE:
					spyral.director.pop()
				elif event.key == pygame.K_UP and self.bossCreeper.direction != DOWN:
					self.bossCreeper.queuedDirection= UP
				elif event.key == pygame.K_LEFT and self.bossCreeper.direction != RIGHT:
					self.bossCreeper.queuedDirection= LEFT
				elif event.key == pygame.K_RIGHT and self.bossCreeper.direction != LEFT:
					self.bossCreeper.queuedDirection= RIGHT
				elif event.key == pygame.K_DOWN and self.bossCreeper.direction != UP:
					self.bossCreeper.queuedDirection= DOWN
				elif event.key == pygame.K_ESCAPE:
					exit(0);
			if event.type == pygame.QUIT:
				spyral.director.pop()
	
		self.group.update()
		
		if not self.isGameover:
			if self.gunpowder.collides(self.bossCreeper):
				newCreeper= CreeperFollower();
				newCreeper.pos= self.creepers[-1].pos;
				self.creepers[-1].follower= newCreeper;
				self.creepers.append(newCreeper);
				self.group.add(newCreeper);
				self.gunpowder.warp();
				self.score.increaseScore();
				sound= random.choice([sounds['creeper1'],sounds['creeper2'],sounds['creeper3'],sounds['creeper4']]);
				sound.play();
			if self.bossCreeper.hittingWall():
				self.bossCreeper.explode();
				self.gameover();
			if self.bossCreeper.hittingCreepers(self.creepers[2:]):
				self.bossCreeper.explode();
				self.gameover();

class Menu(spyral.scene.Scene):
	"""
	Introductory first menu that you see when the game starts.
	"""
	def __init__(self):
		spyral.scene.Scene.__init__(self)
		self.camera = spyral.director.get_camera()
		self.group = spyral.sprite.Group(self.camera)
		bg= images['background'];
		self.camera.set_background(bg)
		
		title = spyral.sprite.Sprite()
		title.image = images['menu_title']
		title.rect.center = self.camera.get_rect().center
		
		instructions = spyral.sprite.Sprite()
		instructions.image = images['menu_instructions']
		instructions.rect.top = title.rect.bottom + 10
		instructions.rect.centerx = self.camera.get_rect().centerx
		
		gameplay1 = spyral.sprite.Sprite()
		gameplay1.image = images['menu_gameplay1']
		gameplay1.rect.top = instructions.rect.bottom + 10
		gameplay1.rect.centerx = self.camera.get_rect().centerx
		
		gameplay2 = spyral.sprite.Sprite()
		gameplay2.image = images['menu_gameplay2']
		gameplay2.rect.top = gameplay1.rect.bottom + 10
		gameplay2.rect.centerx = self.camera.get_rect().centerx
		
		gameplay3 = spyral.sprite.Sprite()
		gameplay3.image = images['menu_gameplay3']
		gameplay3.rect.top = gameplay2.rect.bottom + 10
		gameplay3.rect.centerx = self.camera.get_rect().centerx
		
		self.group.add(title, instructions, gameplay1, gameplay2, gameplay3)
				
	def render(self):
		self.group.draw()
		self.camera.draw()
		
	def update(self, tick):
		for event in pygame.event.get():
			if event.type == pygame.KEYDOWN:
				if event.key == pygame.K_SPACE:
					exit(0)
				elif event.key == pygame.K_n:
					pygame.mixer.music.load(sounds['nyan']);
					pygame.mixer.music.play(-1);
				else:
					spyral.director.push(Sssneak())
			if event.type == pygame.QUIT:
				exit(0)

if __name__ == "__main__":
	spyral.init()
	colors['bg'] = (0, 0, 0);
	colors['score'] = (0, 0, 0)
	colors['game'] = (0, 0, 0)
	colors['menu'] = (0, 0, 0)
	
	geom['size'] = (640, 480)
	geom['width'] = 640
	geom['height'] = 480
	geom['tile_width'] = 32
	geom['tile_height'] = 32
	geom['tiles_across']= geom['width'] / geom['tile_width'];
	geom['tiles_down']= geom['height'] / geom['tile_height'];
	geom['score_in_height'] = .03*geom['height']
	geom['score_font_size'] = .08*geom['height']
	geom['menu_title_font_size'] = .20*geom['height']
	geom['menu_font_size'] = .06*geom['height']
	
	#NOTE: You must initiate the director before you can load images!
	spyral.director.init(geom['size'], ticks_per_second=TICKS_PER_SECOND)

	strings['menu_title'] = "Sssneak!"
	strings['menu_instructions'] = "Press any key except 'N' to start. Press space to quit."
	strings['menu_gameplay1'] = "Use the arrow keys to pick up gunpowder."
	strings['menu_gameplay2'] = "Don't hit your fellow creepers."
	strings['menu_gameplay3'] = "(All resources belong to Mojang or others)"
	strings['game_over1'] = "You died!"
	strings['game_over2'] = "Press space to play again"
		
	fonts['score'] = pygame.font.SysFont(None, geom['score_font_size'])
	fonts['menu'] = pygame.font.SysFont(None, geom['menu_font_size'])
	fonts['game'] = pygame.font.SysFont(None, geom['menu_font_size'])
	fonts['menu_title'] = pygame.font.SysFont(None, geom['menu_title_font_size'])

	creeperSheet= spyral.util.spritesheet(os.path.join('resources','creeper.png'));
	images['creeper-down']= creeperSheet.load_strip( 
							(0,geom['tile_height']*0,
							 geom['tile_width'], geom['tile_height']), 
							4, colorkey=-1);
	images['creeper-left']= creeperSheet.load_strip( 
							(0,geom['tile_height']*1,
							 geom['tile_width'], geom['tile_height']), 
							4, colorkey=-1);
	images['creeper-right']= creeperSheet.load_strip( 
							(0,geom['tile_height']*2,
							 geom['tile_width'], geom['tile_height']), 
							4, colorkey=-1);
	images['creeper-up']= creeperSheet.load_strip( 
							(0,geom['tile_height']*3,
							 geom['tile_width'], geom['tile_height']), 
							4, colorkey=-1);
	explosionSheet= spyral.util.spritesheet(os.path.join('resources','explosion_strip.png'));
	images['explosion']= explosionSheet.load_strip( 
							(0,0,
							 geom['tile_width']*2, geom['tile_height']*2), 
							25, colorkey= -1);
	images['gunpowder']= spyral.util.load_image(os.path.join('resources','gunpowder.png'), colorkey=-1);
	images['grass-tile']= spyral.util.load_image(os.path.join('resources','grey-grass.png'));
	images['background']= spyral.util.new_surface(geom['size'])
	for x in range(geom['tiles_across']):
		for y in range(geom['tiles_down']):
			images['background'].blit(images['grass-tile'], 
									(x*geom['tile_width'],
									 y*geom['tile_height']));
			
	images['menu_title'] = fonts['menu_title'].render(
							strings['menu_title'],
							True,
							colors['menu'])
	images['menu_instructions'] = fonts['menu'].render(
							strings['menu_instructions'],
							True,
							colors['menu'])
	images['menu_gameplay1'] = fonts['menu'].render(
							strings['menu_gameplay1'],
							True,
							colors['menu'])
	images['menu_gameplay2'] = fonts['menu'].render(
							strings['menu_gameplay2'],
							True,
							colors['menu'])
	images['menu_gameplay3'] = fonts['menu'].render(
							strings['menu_gameplay3'],
							True,
							colors['menu'])
	images['game_over1'] = fonts['menu_title'].render(
							strings['game_over1'],
							True,
							colors['game'])
	images['game_over2'] = fonts['game'].render(
							strings['game_over2'],
							True,
							colors['game'])
	
	sounds['creeper1'] = pygame.mixer.Sound(os.path.join('resources','creeper1.ogg'));
	sounds['creeper2'] = pygame.mixer.Sound(os.path.join('resources','creeper2.ogg'));
	sounds['creeper3'] = pygame.mixer.Sound(os.path.join('resources','creeper3.ogg'));
	sounds['creeper4'] = pygame.mixer.Sound(os.path.join('resources','creeper4.ogg'));
	sounds['explode'] = pygame.mixer.Sound(os.path.join('resources','explode.ogg'));
	sounds['piano'] = os.path.join('resources','piano.ogg');
	sounds['nyan'] = os.path.join('resources','nyan.ogg');
	
	pygame.mixer.music.load(sounds['piano']);
	pygame.mixer.music.play(-1);
							
	spyral.director.push(Menu())
	spyral.director.clock.use_wait = False
	spyral.director.run()
