#!/usr/local/bin/python
"""zephulor.py

   Adventures on Planet Zephulor by Aaron Hachez. 

   All code, artwork, and sound copyright (C) 2003, Aaron Hachez.

   ----------------------------------------------------------------------------------------

   This file is part of "Adventures on Planet Zephulor".

   A copy of the GNU General Public License should be included with any
   redistribution of this program in a file named "COPYING.txt".

   "Adventures on Planet Zephulor" 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.

   "Adventures on Planet Zephulor" 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 "Adventures on Planet Zephulor"; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   ----------------------------------------------------------------------------------------

   "Adventures on Planet Zephulor" uses the following libraries:

   PyGame
   SDL

   Both are at the time of this writing, licensed under the GNU LGPL. Please see
   the included file LGPL for details 
"""

# init the SDL window
import pygame, os, sys, string
from pygame.locals import *

# We load the config first
from game_config import *

# The level to load at start
startlevel = 'paxapart_flats-01.data'

# Get the path to this script to find path to data files
config.datadir = os.path.dirname(sys.argv[0])
config.datadir = os.path.abspath(config.datadir)
config.datadir = os.path.join(config.datadir, 'data')

# First we need to look and see if there is a dir for config and save files
# if not, we need to create it. By default we will create it in the user's home dir
# under any OS that has an "os.environ['HOME']" var. Otherwise, create it in the
# directory where the game resides.
try:
    config.confdir = os.path.join(os.environ['HOME'], ".zephulor")
except:
    config.confdir = os.path.dirname(sys.argv[0])
    config.confdir = os.path.abspath(config.confdir)
    config.confdir = os.path.join(config.confdir, 'conf')

if not os.access(config.confdir, os.F_OK):
    os.mkdir(config.confdir)
    os.chmod(config.confdir, 0700)

if not os.access(config.confdir, os.W_OK) or not os.access(config.confdir, os.X_OK) or not os.access(config.confdir, os.R_OK):
    print "The configuration directory:\n"
    print config.confdir
    print "\ndoes not appear to have the correct permissions,"
    print "please verify that you have read, write and execute permissions for that directory."

# Load the configuration data -- This is the only location where we specified the config filename
config.load('main.cfg')

pygame.init()

# Check to see whether some modules that could cause problems initialized properly
if not pygame.font.get_init(): 
    print "Error, font init failed, game may not run properly"
if not pygame.mixer.get_init(): 
    print "Error initializing sound system. Sound disabled."
    print "If your sound card is properly configured and works in other applications,"
    print "please email: bugs@hollowworks.com with the details of this error message,"
    print "and your system specifications (Operating system, sound card, drivers, etc...)"
    config.nomusic = 1
    config.nosfx = 1

# This checks for command line arguments
for item in sys.argv:
    # Code for the following two items contributed by Andy Meier -- Added 2004.10.07
    if item in ("--help", "-h"):
        print "Usage: zephulor.py [OPTIONS]"
        print "Adventures on Planet Zephulor -- a side scrolling jump and shoot game."
        print
        print "Options:"
        print "  -h, --help             output this help page and exit"
        print "  -v, --version          output version information and exit"
        print "      --no-sound         disable audio"
        print "      --no-parallax      disable parallax scrolling"
        print "      --speed=X          adjust frame delay (Game speed)"
        print "                         default speed is 20 ticks"
        print "                         speed 0 runs the game at the maximum rate"
        print "      --level=FILENAME   start game at the specified level"
        print
        print "Report bugs to <bugs@hollowworks.com>."
        sys.exit()
    elif item in ("--version", "-v"):
        print "Adventures on Planet Zephulor 2004.10.23"
        print "Written by Aaron Hachez."
        print "With contributions by Andy Meier"
        print
        print "Copyright 2004 (C) Aaron Hachez."
        print "This is free software; see the source for copying conditions.  There is NO"
        print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
        sys.exit()
    elif item == "--no-sound":
        config.nomusic = 1
        config.nosfx = 1
    elif item == "--no-parallax":
        config.noparallax = 1
    elif string.find(item, "--speed=") > -1:
        array = string.split(item, "=")
        config.gamespeed = string.atoi(array[1])
    elif string.find(item, "--level=") > -1:
        # If specified, change starting level to user specified
        array = string.split(item, "=")
        startlevel = array[1]

# These must follow the init
from game_entities import *
from game_map import *
from game_sound import *
from game_hud import *
from game_menu import *
from myimg import *
        
def savegame(map, filename):
    """Save the game data for later load"""

    filename = os.path.join(config.confdir, filename)

    try:
        f = file(filename, "w")

        cPickle.dump(map.mapfile, f)
        cPickle.dump(map.viewport, f)

        cPickle.dump(map.playerchar.screenrect, f)
        cPickle.dump(map.playerchar.begscreenrect, f)
        cPickle.dump(map.playerchar.maprect, f)
        cPickle.dump(map.playerchar.begmaprect, f)
        cPickle.dump(map.playerchar.boundrect, f)
        cPickle.dump(map.playerchar.begboundrect, f)
        cPickle.dump(map.playerchar.state, f)
        cPickle.dump(map.playerchar.hitpoints, f)
        cPickle.dump(map.playerchar.xvel, f)
        cPickle.dump(map.playerchar.xtvel, f)
        cPickle.dump(map.playerchar.yvel, f)
        cPickle.dump(map.playerchar.ytvel, f)
        cPickle.dump(map.playerchar.cur_anim, f)
        cPickle.dump(map.playerchar.cur_cel, f)
        cPickle.dump(map.playerchar.dead, f)
        cPickle.dump(map.playerchar.dying, f)
        cPickle.dump(map.playerchar.dodamage, f)
        cPickle.dump(map.playerchar.bulletdamage, f)
        cPickle.dump(map.playerchar.jumprate, f)
        cPickle.dump(map.playerchar.score, f)

        for char in map.charlist:
            cPickle.dump(char.screenrect, f)
            cPickle.dump(char.maprect, f)
            cPickle.dump(char.boundrect, f)
            cPickle.dump(char.state, f)
            cPickle.dump(char.hitpoints, f)
            cPickle.dump(char.xvel, f)
            cPickle.dump(char.xtvel, f)
            cPickle.dump(char.yvel, f)
            cPickle.dump(char.ytvel, f)
            cPickle.dump(char.cur_anim, f)
            cPickle.dump(char.cur_cel, f)
            cPickle.dump(char.dead, f)
            cPickle.dump(char.dying, f)
            cPickle.dump(char.firebonus, f)
            cPickle.dump(char.jumpbonus, f)
            cPickle.dump(char.invulnbonus, f)

        f.close()
    except:
        print "There was an error saving game to file:", filename

def prefetchload(filename):
    """Fetch the map name for the saved game to be used"""

    filename = os.path.join(config.confdir, filename)

    mapname = None

    try:
        f = file(filename, "r")
    except:
        print "Unable to open file for map name prefetch:", filename
        return(None)

    mapname = cPickle.load(f)
    f.close()

    return(mapname)

def loadgame(map, filename):
    """Load saved game"""

    filename = os.path.join(config.confdir, filename)

    try:
        f = file(filename, "r")
    except:
        print "There was an error loading game from file:", filename
        return(None)

    mapfile = cPickle.load(f)
    map.viewport = cPickle.load(f)

    map.playerchar.screenrect = cPickle.load(f)
    map.playerchar.begscreenrect = cPickle.load(f)
    map.playerchar.maprect = cPickle.load(f)
    map.playerchar.begmaprect = cPickle.load(f)
    map.playerchar.boundrect = cPickle.load(f)
    map.playerchar.begboundrect = cPickle.load(f)
    map.playerchar.state = cPickle.load(f)
    map.playerchar.hitpoints = cPickle.load(f)
    map.playerchar.xvel = cPickle.load(f)
    map.playerchar.xtvel = cPickle.load(f)
    map.playerchar.yvel = cPickle.load(f)
    map.playerchar.ytvel = cPickle.load(f)
    map.playerchar.cur_anim = cPickle.load(f)
    map.playerchar.cur_cel = cPickle.load(f)
    map.playerchar.dead = cPickle.load(f)
    map.playerchar.dying = cPickle.load(f)
    map.playerchar.dodamage = cPickle.load(f)
    map.playerchar.bulletdamage = cPickle.load(f)
    map.playerchar.jumprate = cPickle.load(f)
    map.playerchar.score = cPickle.load(f)

    mainhud.pscore = map.playerchar.score    

    for char in map.charlist:
        char.screenrect = cPickle.load(f)
        char.maprect= cPickle.load(f)
        char.boundrect = cPickle.load(f)
        char.state = cPickle.load(f)
        char.hitpoints = cPickle.load(f)
        char.xvel = cPickle.load(f)
        char.xtvel = cPickle.load(f)
        char.yvel = cPickle.load(f)
        char.ytvel = cPickle.load(f)
        char.cur_anim = cPickle.load(f)
        char.cur_cel = cPickle.load(f)
        char.dead = cPickle.load(f)
        char.dying = cPickle.load(f)
        char.firebonus = cPickle.load(f)
        char.jumpbonus = cPickle.load(f)
        char.invulnbonus = cPickle.load(f)

    f.close()

def cutscene(scenefile, screen, wait=0):
    """This shows a cutscene. If wait is non-zero, pause between each frame"""

    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((0, 0, 0))

    # Add in the full path
    scenefile = os.path.join(config.datadir, scenefile)

    try:
        # Open the scene file and read it in
        f = file(scenefile, 'r')
    except:
        print "Unable to open scene file: ", scenefile
        return(0);

    # Get the number of entries in the scene file
    entries = cPickle.load(f)

    # Get the next map if there is one
    nextmap = cPickle.load(f)

    # See if the scene says to wait
    scnwait = cPickle.load(f)

    # If the scene says to wait
    if scnwait != None:
        wait = 1

    # This flag is for exit of a wait loop
    endloop = 0

    # For each entry
    for i in range(entries):
        entry = cPickle.load(f)
        image = loadimage(entry)

        # Center the image on the background
        x = (background.get_width() - image.get_width()) / 2
        y = (background.get_height() - image.get_height()) / 2

        # Copy the cutscene image onto the background
        background.blit(image, (x, y))

        screen.blit(background, (0,0))

        pygame.display.flip()

        # If wait has been set
        if wait != 0:

            while(not endloop):
                for event in pygame.event.get():
                    if event.type == KEYDOWN:
                        if event.key == K_ESCAPE:
                            endloop = 2
                        else:
                            endloop = 1
            endloop = 0
 
        # If the endloop flag is set to 2, that means escape the whole scene file
        if endloop == 2:
            break

    f.close()

    return(nextmap)


# What music we were playing last time.
oldmusic = None
map1 = map_base()
paused = 0

def main(loadinternal="no"):

    global map1, paused, oldmusic

    # This variable tells the system whether or not to reload on exit, i.e. for screen width changes
    reload = 0

    # Now that the config is loaded, set all of the keys
    #keyup = config.key('Up') -- we don't actually need
    keydown = config.key('Kneel')
    keyleft = config.key('Left')
    keyright = config.key('Right')
    keyjump = config.key('Jump')
    keyfire = config.key('Fire')
    keypause = config.key('Pause')
    keyqquit = config.key('QuickQuit')
    keysave = config.key('QuickSave')
    keyload = config.key('QuickLoad')

    if config.fullscreen == 1:
        screen = pygame.display.set_mode((config.screenwidth, config.screenheight), FULLSCREEN|HWSURFACE|DOUBLEBUF)
    else:
        screen = pygame.display.set_mode((config.screenwidth, config.screenheight), HWSURFACE|DOUBLEBUF)

    pygame.display.set_caption('Adventures on Planet Zephulor')
    pygame.mouse.set_visible(0)

    cutscene("startup.scn", screen)

    # If load internal is not true, load the default start map
    if loadinternal == "no":

#        # Get the name of the map that is used in the load file
#        mapname = prefetchload("002.sav")
#
#        if mapname != None:
#            # If the map name does not match the current map, load the map in question
#            if not map1.mapfile == mapname:
#                del map1
#                map1 = map_base()
#                map1.load(mapname, viewrect = [ 0, 0, config.screenwidth, config.screenheight ])
#                music.load(map1.musicfile)
#                # Set the volume for music
#                music.setvol(config.musicvol)
#            
#            # Now load the game    
#            loadgame(map1, "002.sav")
#        else:
#
         map1.load(startlevel, viewrect = [ 0, 0, config.screenwidth, config.screenheight])

    # Else if it is true, load internal save
    elif loadinternal == "yes":
        # Get the name of the map that is used in the load file
        mapname = prefetchload("internal.sav")

        # Make sure the save file exists
        if mapname != None:
            # Finally load our game data
            loadgame(map1, "internal.sav")

    mainhud.load('heart.bmp', (10, 4), 'bullet1.bmp', (10, 20), 'eheart.bmp', (10, 460), (550, 455))

    # Setup the menu

    menuw = 256
    menuh = 384
    menux1 = (config.screenwidth - menuw) / 2
    menuy1 = (config.screenheight - menuh) / 2
    menux2 = menux1 + menuw
    menuy2 = menuy1 + menuh
    mainmenu = menu_base(menux1, menuy1, menux2, menuy2, levels=4)

    # Main menu
    mainmenu.additem(0, (menuy1, 32), 'Play', 998)
    mainmenu.additem(0, (menuy1 + 32, 32), 'Start New', 994)
    mainmenu.additem(0, (menuy1 + 64, 32), 'Setup', 1)
    mainmenu.additem(0, (menuy1 + 96, 32), 'Save', 996)
    mainmenu.additem(0, (menuy1 + 128, 32), 'Load', 995)
    mainmenu.additem(0, (menuy1 + 160, 32), 'Fullscreen', 997)
    mainmenu.additem(0, (menuy1 + 192, 32), 'Credits', 993)
    mainmenu.additem(0, (menuy1 + 256, 32), 'Quit', 999)

    # Setup menu
    mainmenu.additem(1, (menuy1, 32), 'Controls', 2)
    mainmenu.additem(1, (menuy1 + 32, 32), 'Sound', 3)
    mainmenu.additem(1, (menuy1 + 96, 32), 'Back', 0)

    # Setup the clock
    clock = pygame.time.Clock()

    # Load the music
    music.load(map1.musicfile)

    # Record the name of the music file
    oldmusic = map1.musicfile

    # Set the volume for music
    music.setvol(config.musicvol)

    # Load up the pause image
    pauseimg = myimg.loadimage('pause.bmp', colorkey=-1)
    pausex = 0
    pausey = 0
    pausex = pauseimg.get_width()
    pausey = pauseimg.get_height()
    pausex = (config.screenwidth - pausex) / 2
    pausey = (config.screenheight - pausey) / 2

    keyflag = 0
    quit = 0
    menu = 0
    

    if config.firstrun == 1:
        cutscene('intro.scn', screen, wait=1)

    music.play()

    while not quit:
    
        clock.tick(config.gamespeed)

        music.loop()
    
        for event in pygame.event.get():
            if event.type == QUIT:
                quit = 1
            elif event.type == KEYDOWN:

                if mainmenu.waiting:
                    # This prevents the user from overriding the menu key, i.e. they can't assign escape
                    if event.key != K_ESCAPE:
                        mainmenu.getinput(event.key)

                        # Reset the key mappings
                        #keyup = config.key('Up') -- We don't actually need
                        keydown = config.key('Kneel')
                        keyleft = config.key('Left')
                        keyright = config.key('Right')
                        keyjump = config.key('Jump')
                        keyfire = config.key('Fire')
                        keypause = config.key('Pause')
                        keyqquit = config.key('QuickQuit')
                        keysave = config.key('QuickSave')
                        keyload = config.key('QuickLoad')

                else:
                    if event.key == keypause:
                        # Pause/Unpause
                        if paused == 0:
                            paused = 1
                        else:
                            paused = 0
    
                    # Show the menu
                    if event.key == K_ESCAPE:
                        if menu == 0:
                            menu = 1
                            
                            # Reset the menu to the first level
                            mainmenu.menulevel = 0
                            
                            # Make the mouse visible
                            pygame.mouse.set_visible(1)
                
                            #cutscene('logo.scn', screen)
                        else:
                            menu = 0
    
                            # Hide the mouse
                            pygame.mouse.set_visible(0)
    
                    # Pressing this key means quick quit
                    if event.key == keyqquit:
                        quit = 1
    
                    # Only allow the player to do things if we are not paused
                    if not paused:
                        #if event.key == keyup: -- un-needed
                        #    pass
                        if event.key == keydown:
                            map1.playerchar.kneel_on()
                        elif event.key == keyleft:
                            map1.playerchar.left_on()
                        elif event.key == keyright:
                            map1.playerchar.right_on()
                        elif event.key == keyjump:
                            map1.playerchar.jump_on()
                            map1.playerchar.jumpkey = 1
                        elif event.key == keyfire:
                            map1.playerchar.addbullet()
                        elif event.key == keysave:
                            cutscene("save.scn", screen)
                            savegame(map1, "000.sav")
                        elif event.key == keyload:
                            cutscene("load.scn", screen)

                            # Get the name of the map that is used in the load file
                            mapname = prefetchload("000.sav")

                            # Make sure that we were able to load the save file
                            if mapname != None:
                                # If the map name does not match the current map, load the map in question
                                if not map1.mapfile == mapname:
                                    del map1
                                    map1 = map_base()
                                    map1.load(mapname, viewrect = [ 0, 0, config.screenwidth, config.screenheight ])
                                    if not map1.musicfile == oldmusic:
                                        oldmusic = map1.musicfile
                                        music.load(map1.musicfile)
                                        # Set the volume for music
                                        music.setvol(config.musicvol)
                                
                                # Now load the game    
                                loadgame(map1, "000.sav")

            elif event.type == KEYUP:
                #if event.key == keyup: -- Un-needed
                #    pass
                if event.key == keydown:
                    map1.playerchar.kneel_off()
                if event.key == keyleft:
                    map1.playerchar.left_off()
                    pass
                if event.key == keyright:
                    map1.playerchar.right_off()
                    pass
                if event.key == keyjump:
                    map1.playerchar.jump_off()
                    map1.playerchar.jumpkey = 0

            elif event.type == MOUSEBUTTONDOWN:
                if menu != 0:
                    result = mainmenu.getclick(pygame.mouse.get_pos())

                    # Quit if result is 999
                    if result == 999:
                        quit = 1
                    # Else, leave menu
                    elif result == 998:
                        # Hide the mouse
                        pygame.mouse.set_visible(0)

                        menu = 0
                    elif result == 997:
                        if config.fullscreen == 0:
                            config.fullscreen = 1
                        else:
                            config.fullscreen = 0

                        # Do an internal save
                        savegame(map1, "internal.sav")

                        quit = 1
                        reload = 2
                    elif result == 996:
                        cutscene("save.scn", screen)
                        savegame(map1, "001.sav")

                        menu = 0

                        # Hide the mouse
                        pygame.mouse.set_visible(0)
     
                    elif result == 995:
                        cutscene("load.scn", screen)

                        # Get the name of the map that is used in the load file
                        mapname = prefetchload("001.sav")

                        if mapname != None:
                            # If the map name does not match the current map, load the map in question
                            if not map1.mapfile == mapname:
                                del map1
                                map1 = map_base()
                                map1.load(mapname, viewrect = [ 0, 0, config.screenwidth, config.screenheight ])
                                music.load(map1.musicfile)
                                # Set the volume for music
                                music.setvol(config.musicvol)
                            
                            # Now load the game    
                            loadgame(map1, "001.sav")

                        menu = 0

                        # Hide the mouse
                        pygame.mouse.set_visible(0)

                    # This will reload the game at the beginning.
                    elif result == 994: 
                        reload = 1
                        quit = 1

                    elif result == 993: 
                        # Hide the mouse
                        pygame.mouse.set_visible(0)
                        cutscene('credits.scn', screen)
                        menu = 0

                    # If the mouse button was pressed, volume may have changed, reload it
                    music.setvol(config.musicvol)
                    entsfxlist.setvol(config.soundvol)

            elif event.type == MOUSEBUTTONUP:
                pass

        if not quit:
            if not paused and not menu:
                # Handle messages
                if map1.state == 3:
                    cutscene("load.scn", screen)

                    # Record next map name
                    nextmap = map1.nextmap

                    # Record player stats
                    hp = map1.playerchar.hitpoints
                    bdmg = map1.playerchar.bulletdamage
                    ddmg = map1.playerchar.dodamage

                    # If the next map is a scene, play it instead, returning the next map to load
                    stringlen = len(nextmap)
                    if nextmap[stringlen - 3: stringlen] == "scn":
                        nextmap = cutscene(nextmap, screen)

                    if nextmap != None:
                        del map1
                        map1 = map_base()
                        result = map1.load(nextmap, viewrect = [ 0, 0, config.screenwidth, config.screenheight ])
                        if not map1.musicfile == oldmusic:
                            oldmusic = map1.musicfile
                            music.load(map1.musicfile)
                            # Set the volume for music
                            music.setvol(config.musicvol)
                        
                        # Give the player whatever hit point level had in last level
                        map1.playerchar.hitpoints = hp
                        map1.playerchar.bulletdamage = bdmg
                        map1.playerchar.dodamage = ddmg
                    else:
                        quit = 1

                    continue
        
                map1.sync()
                
                # Make a list of tiles for map draw and coldet routines
                map1.makelist()
            
                # Draw the map to the screen
                map1.draw(screen)
        
                # Draw the hud to the screen
                mainhud.draw(screen)
    
            if paused:
                screen.blit(pauseimg, (pausex, pausey))
        
            if menu:
                mainmenu.draw(screen)
    
            pygame.display.flip()

    # After quit, cleanup
    config.save()

    if quit and not reload:
        #savegame(map1, "002.sav")
        #cutscene("exit.scn", screen, wait=1)
        pass

    return(reload)

result = 1
while(result):
    if result == 2:
        result = main(loadinternal="yes")
    else:
        result = main()
