#!/usr/bin/env/python # Tells Linux systems how to run this file. __author__ = "Kris Schnee" __version__ = "2010.2.5" __license__ = "GPLv3" ##### Imported Modules ##### ## Standard import time import os ## Third-party import pygame from pygame.locals import * ## Mine ##### Constants ##### ## Level size in tiles LEVEL_SIZE_X = 100 LEVEL_SIZE_Y = 25 ## Tile size in pixels TILE_SIZE_X = 50 TILE_SIZE_Y = 50 TILE_HEIGHT_INCREMENT = TILE_SIZE_Y / 10.0 PLAYER_SIZE = (TILE_SIZE_X-.1,TILE_SIZE_Y-.1) PLAYER_DRAW_OFFSET = (-PLAYER_SIZE[0]/2,-PLAYER_SIZE[1]) CAMERA_OFFSET_X = 0 CAMERA_OFFSET_Y = TILE_SIZE_Y*2 SCREEN_SIZE = (min(800,LEVEL_SIZE_X*TILE_SIZE_X),min(600,LEVEL_SIZE_Y*TILE_SIZE_Y)) os.environ["SDL_VIDEO_CENTERED"] = "1" ## Center the graphics window. screen = pygame.display.set_mode(SCREEN_SIZE) pygame.display.set_caption("Squirrelly Movement Demo") TILES = {} BACKGROUND = None try: ## Load graphics TILES["block"] = pygame.image.load(os.path.join("graphics","t_block.png")).convert() TILES["block"].set_colorkey((255,0,255)) TILES["ice"] = pygame.image.load(os.path.join("graphics","t_ice.png")).convert() TILES["ice"].set_colorkey((255,0,255)) TILES["wall"] = pygame.image.load(os.path.join("graphics","t_wall.png")).convert() TILES["wall"].set_colorkey((255,0,255)) TILES["checker"] = pygame.image.load(os.path.join("graphics","t_checker.png")).convert() TILES["checker"].set_colorkey((255,0,255)) for n in range(1,10): t_block = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_block.set_colorkey((0,0,0)) t_block.blit(TILES["checker"],(0,TILE_HEIGHT_INCREMENT*(10-n)),(0,0,TILE_SIZE_X,TILE_SIZE_Y)) TILES["block"+str(n)] = t_block BACKGROUND = pygame.image.load(os.path.join("graphics","background.png")).convert() PLAYER_DRAW_OFFSET = (-100,-100) except: ## Improvised graphics t_block = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_block.fill((32,128,64)) TILES["block"] = t_block for n in range(1,10): t_block = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_block.set_colorkey((0,0,0)) y = TILE_SIZE_Y - int(TILE_HEIGHT_INCREMENT*n) y2 = TILE_SIZE_Y - y pygame.draw.rect(t_block,(64,64,64),(0,y,TILE_SIZE_X,y2),0) TILES["block"+str(n)] = t_block t_ice = pygame.surface.Surface((TILE_SIZE_X,TILE_SIZE_Y)) t_ice.fill((200,200,255)) TILES["ice"] = t_ice BACKGROUND = pygame.surface.Surface(SCREEN_SIZE) for y in range(SCREEN_SIZE[1]): pygame.draw.line(BACKGROUND,(0,0,255-(y/4)),(0,y),(SCREEN_SIZE[0],y)) PLAYER_SPRITE = pygame.surface.Surface(PLAYER_SIZE) PLAYER_SPRITE.set_colorkey((0,0,0)) ## pygame.draw.rect(PLAYER_SPRITE,(255,0,0),(0,0,PLAYER_SIZE[0],PLAYER_SIZE[1]),1) PLAYER_SPRITE.fill((255,255,0)) ##try: ## spritesheet = pygame.image.load("squirrel_sprite_100.png").convert() #### spritesheet.set_colorkey((255,0,255)) ## PLAYER_SPRITE = pygame.surface.Surface((200,100)) ## PLAYER_SPRITE.blit(spritesheet,(0,0),(0,0,200,100)) ## PLAYER_SPRITE.set_colorkey((255,0,255)) ## PLAYER_DRAW_OFFSET = (-100,-100) ##except: ## pass LOAD_LEVEL_GUIDE = {" ":"blank", ".":"block", "i":"ice", "c":"checker", } for n in range(1,10): LOAD_LEVEL_GUIDE[str(n)] = "block"+str(n) TILE_DICT = {" ":None, ".":TILES["block"], "i":TILES["ice"], "c":TILES["checker"], } for n in range(1,10): TILE_DICT[str(n)] = TILES["block"+str(n)] ## Physics FALL_SPEED = .1 CLIMB_SPEED = .4 JUMP_SPEED = -0.8 TERMINAL_VELOCITY = 0.49 TERMINAL_VELOCITY_GLIDING = 0.2 RUN_SPEED = 0.40 RUN_ACCEL = .05 MIN_SPEED = .1 WALL_JUMP_AIR_CONTROL_BLOCKED = 5 WALL_JUMP_SPEED = RUN_SPEED * 2.0 TERRAINS = {"block":{"slipping":.5}, "ice":{"slipping":.8}, "checker":{"slipping":.67}, } TERRAINS_SOLID = ["block","ice","checker"] STAIR_HEIGHT = {"block":1.0,"ice":1.0,"checker":1.0} for n in range(1,10): STAIR_HEIGHT["block"+str(n)] = n/10.0 ## TERRAINS_SOLID.append("block"+str(n)) TERRAINS["block"+str(n)] = {"slipping":.5} TERRAIN_EMPTY = "empty" MAX_STAIRSTEP = .5 ABOVEGROUND_MARGIN = .1 WALL_GRAB_DIST = .2 ## Controls K_DASH = K_LSHIFT K_GRAB = K_z ## Audio MUSIC_END_EVENT = pygame.USEREVENT + 1 pygame.mixer.music.set_endevent(MUSIC_END_EVENT) ##### Classes ##### class Squirrel: def __init__(self,**options): self.coords = list(options.get("coords",[3,LEVEL_SIZE_Y-3])) self.speed_x = 0.0 self.speed_y = 0.0 self.size_x = 0.9 self.size_y = 1.9 self.half_x = self.size_x / 2.0 self.half_y = self.size_y / 2.0 self.dash = False self.jumping = False self.air_control_disabled = 0 ## Number of frames left till restored self.ignore_jump = False self.already_started_wallgrab = False self.wallgrab = False self.sprite_frames = LoadSprite("squirrel_sprite_100.png",(200,100),2,2) self.current_frame = 1 def SetAction(self,action,direction_right=True): """Sets my animation frame based on what I'm doing. This is still ad-hoc for demo purposes, very basic.""" if action == "walk": if direction_right: self.current_frame = 0 else: self.current_frame = 2 elif action == "dash": if direction_right: self.current_frame = 1 else: self.current_frame = 3 else: if direction_right: ## Not available; use default self.current_frame = 0 else: self.current_frame = 2 class Game: def __init__(self): self.clock = pygame.time.Clock() self.done = False self.level = [] for x in range(LEVEL_SIZE_X): self.level.append([]) for y in range(LEVEL_SIZE_Y): self.level[-1].append("blank") self.stairmap = [] for x in range(LEVEL_SIZE_X): self.stairmap.append([]) for y in range(LEVEL_SIZE_Y): self.stairmap[-1].append(.1) self.tilemap = [] for x in range(LEVEL_SIZE_X): self.tilemap.append([]) for y in range(LEVEL_SIZE_Y): self.tilemap[-1].append(None) ## Player object self.player = Squirrel() self.frames = 0 self.start_time = time.time() self.LoadLevel("level") def LoadLevel(self,name="level"): if not name.endswith(".txt"): name += ".txt" f = open(name,"r") text = f.read() f.close() ## Level size? if text.endswith("\n"): text = text[:-1] self.level_height = text.count("\n")+1 self.level_width = text.find("\n") text = text.replace("\n","") self.level = [] for x in range(self.level_width): self.level.append([]) for y in range(self.level_height): self.level[-1].append( LOAD_LEVEL_GUIDE[ text[(self.level_width*y)+x] ] ) self.stairmap = [] for x in range(self.level_width): self.stairmap.append([]) for y in range(self.level_height): block_type = self.level[x][y] self.stairmap[-1].append( STAIR_HEIGHT.get(block_type,0.0) ) ## Stores references to the tiles to draw. self.tilemap = [] for x in range(self.level_width): self.tilemap.append([]) for y in range(self.level_height): self.tilemap[-1].append( TILE_DICT[ text[(self.level_width*y)+x] ] ) def Draw(self): ## Background screen.blit(BACKGROUND,(0,0)) player_draw_coords = (int(SCREEN_SIZE[0]/2.0)+PLAYER_DRAW_OFFSET[0]+CAMERA_OFFSET_X, int(SCREEN_SIZE[1]/2.0)+PLAYER_DRAW_OFFSET[1]+CAMERA_OFFSET_Y) player_pixel_coords = (self.player.coords[0]*TILE_SIZE_X, self.player.coords[1]*TILE_SIZE_Y) ## print "Drawing player at "+str(player_draw_coords) self.camera_location = (player_pixel_coords[0]-(SCREEN_SIZE[0]/2)-CAMERA_OFFSET_X,player_pixel_coords[1]-(SCREEN_SIZE[1]/2)-CAMERA_OFFSET_Y) self.tiles_drawable = ((screen.get_width() / TILE_SIZE_X)+2, (screen.get_height() / TILE_SIZE_Y)+2) upperleft_tile = [ int(max(0,self.camera_location[0] / TILE_SIZE_X)), int(max(0,self.camera_location[1] / TILE_SIZE_Y)) ] ## Tiles for y in range(self.level_height): for x in range(self.level_width): tile = self.tilemap[x][y] if tile: draw_pos = ((x*TILE_SIZE_X)-self.camera_location[0], (y*TILE_SIZE_Y)-self.camera_location[1]+2) screen.blit(tile,draw_pos) ## screen.blit(tile,(TILE_SIZE_X*x,TILE_SIZE_Y*y)) ## Player pc = self.player.coords ## player_draw_coords = (TILE_SIZE_X*pc[0])+PLAYER_DRAW_OFFSET[0], (TILE_SIZE_Y*pc[1])+PLAYER_DRAW_OFFSET[1] screen.blit(self.player.sprite_frames[self.player.current_frame],player_draw_coords) ## screen.blit(self.player.sprite_frames[self.player.current_frame],player_draw_coords) pygame.display.update() def Logic(self): ## print "\nFrame: "+str(self.frames) ## What keys are currently held? keys_down = pygame.key.get_pressed() if keys_down[K_ESCAPE]: self.done = True self.Physics(keys_down) ## React to a few event types; this is also needed to clear input. for event in pygame.event.get(): if event.type == QUIT: self.done = True elif event.type == MUSIC_END_EVENT: print "looping" LoopSong() self.clock.tick(20) def CheckBlocked(self,coords): """Is this block occupied?""" x, y = coords return self.level[ int(x) ][ int(y) ] in TERRAINS_SOLID def GetBlockType(self,x,y): return self.level[ int(x) ][ int(y) ] def GetStairHeight(self,x,y): return self.stairmap[ int(x) ][ int(y) ] def Physics(self,keys_down): """Move the player based on velocity and obstruction.""" ## Where will the player be if nothing interferes? new_x = self.player.coords[0]+self.player.speed_x new_y = self.player.coords[1]+self.player.speed_y ## print "Current & proposed coords:"+str(self.player.coords)+"\n"+str((new_x,new_y)) ## If the following calculations don't raise this, player will fall. player_on_ground = False left_blocked = False right_blocked = False ## Check a set of points around player, then act on a combination. topleft = (new_x - self.player.half_x, new_y - self.player.size_y) topmid = (new_x, new_y - self.player.size_y) topright = (new_x + self.player.half_x, new_y - self.player.size_y) leftmid = (new_x - self.player.half_x, new_y - self.player.half_y) rightmid = (new_x + self.player.half_x, new_y - self.player.half_y) ## print "Checkpoints:\n"+str(topleft)+"\n"+str(topmid)+"\n"+str(topright)+"\n"+str(leftmid)+"\n"+str(rightmid) b_tl = self.CheckBlocked(topleft) b_tm = self.CheckBlocked(topmid) b_tr = self.CheckBlocked(topright) b_lm = self.CheckBlocked(leftmid) b_rm = self.CheckBlocked(rightmid) recalc = False if b_rm or (b_tr and not b_tm): recalc = True ## print "blocked right" right_blocked = True ## For ledge-grabs self.player.speed_x = 0.0 new_x = float(int(rightmid[0])) - self.player.half_x - .01 elif b_lm or (b_tl and not b_tm): recalc = True ## print "blocked left" left_blocked = True ## For ledge-grabs self.player.speed_x = 0.0 new_x = float(int(leftmid[0])) + 1.0 + self.player.half_x + .01 if recalc: ## print "Recalculating top checkpoints." topleft = (new_x - self.player.half_x, new_y - self.player.size_y) topmid = (new_x, new_y - self.player.size_y) topright = (new_x + self.player.half_x, new_y - self.player.size_y) b_tl = self.CheckBlocked(topleft) b_tm = self.CheckBlocked(topmid) b_tr = self.CheckBlocked(topright) if b_tl or b_tm or b_tr: ## print "Blocked above (hopefully)" self.player.speed_y = 0.0 new_y = float(int(new_y)) + 1.0 ## Check two points beside the player for wall-grabbing purposes. leftgrab = self.CheckBlocked((new_x - self.player.half_x - WALL_GRAB_DIST, new_y - self.player.half_y)) rightgrab = self.CheckBlocked((new_x + self.player.half_x + WALL_GRAB_DIST, new_y - self.player.half_y)) ## Check one or more points below the player. ## print "Checking below." x = new_x ##self.player.coords[0] ## Note: Current x. down_ys = [self.player.coords[1]-.1] drop_distance = self.player.speed_y DROP_CHECK_INCREMENT = .5 while drop_distance > DROP_CHECK_INCREMENT: down_ys.append(down_ys[-1]+DROP_CHECK_INCREMENT) drop_distance -= DROP_CHECK_INCREMENT down_ys.append(new_y) ## print "Y points we'll check: "+str(down_ys) for y in down_ys: ## print "Checking y: "+str(y) block_height = self.GetStairHeight(x,y) ## print "Block height of ("+str(x)[:5]+","+str(y)[:5]+"): "+str(block_height) if block_height == 1.0: ## print "This is a full block. We'll stop here." abs_height = float(int(y)) ## print "Player y vs. stair y: "+str(y)+", "+str(abs_height) if y > abs_height: ## print "Player is embedded in block. Adjust y & stop fall." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points elif abs(y-abs_height)<.05: ## print "Player is exactly atop block." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points else: ## print "Player is above block. That's okay." pass elif block_height > 0.0: ## print "This is a partial block (height "+str(block_height)+"). We'll stop here." abs_height = float(int(y)) + 1.0 - block_height ## print "Player y vs. stair y: "+str(y)+", "+str(abs_height) if y > abs_height: ## print "Player is embedded in partial block. Adjust y & stop fall." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points elif abs(y-abs_height)<.05: ## print "Player is exactly atop block." self.player.speed_y = 0.0 new_y = abs_height player_on_ground = True break ## Done checking points else: ## print "Player is above block. That's okay." pass ## print "Player on ground: "+str(player_on_ground) if not player_on_ground: ## Fall! self.player.speed_y = min(self.player.speed_y+FALL_SPEED,TERMINAL_VELOCITY) else: ground = self.GetBlockType(new_x,new_y) ## bottom_x_left = self.player.coords[0] - self.player.half_x ## bottom_x_mid = self.player.coords[0] ## bottom_x_right = self.player.coords[0] + self.player.half_x ## bottom_y = self.player.coords[1] ##+ .01 ## ## footing_left = self.GetBlockType(bottom_x_left,bottom_y) ## footing_mid = self.GetBlockType(bottom_x_mid,bottom_y) ## footing_right = self.GetBlockType(bottom_x_right,bottom_y) ## height_left = self.GetStairHeight(bottom_x_left,bottom_y) ## height_mid = self.GetStairHeight(bottom_x_mid,bottom_y) ## height_right = self.GetStairHeight(bottom_x_right,bottom_y) ## print footing_left, footing_mid, footing_right ## print "Base: "+str(int(bottom_y))+", l/m/r: "+str((height_left, height_mid, height_right)) ## #### if not height_left or height_mid or height_right: ## if not (height_left or height_mid or height_right): ## ## Not standing on anything. Fall. ## self.player.speed_y = min(self.player.speed_y+FALL_SPEED,TERMINAL_VELOCITY) ## else: ## player_on_ground = True ## ## Decide what terrain we're effectively on for friction &c. ## ground = footing_mid or footing_left or footing_right ## self.player.speed_y = 0.0 ## self.player.new_y = self.player.coords[1] ## print "Grounded: "+str(player_on_ground) ## print self.player.coords ## Controls m_left = False m_right = False m_up = False m_down = False m_jump = False m_grab = False ledge_hanging = False keys_down = pygame.key.get_pressed() if keys_down[K_ESCAPE]: self.done = True if keys_down[K_RIGHT]: m_right = True if keys_down[K_LEFT]: m_left = True if keys_down[K_UP]: m_up = True if keys_down[K_DOWN]: m_down = True self.player.dash = keys_down[K_DASH] if keys_down[K_SPACE] or keys_down[K_x]: m_jump = True if keys_down[K_GRAB]: m_grab = True ## Hang onto walls if (not player_on_ground) and (leftgrab or rightgrab): self.player.speed_y = 0.0 self.player.wallgrab = True if not self.player.already_started_wallgrab: self.player.ignore_jump = True ## Wait till Jump is released self.player.already_started_wallgrab = True if m_up: self.player.speed_y = -CLIMB_SPEED elif m_down: self.player.speed_y = CLIMB_SPEED else: self.player.already_started_wallgrab = False self.player.wallgrab = False self.player.ignore_jump = False if m_jump: if not self.player.ignore_jump: if player_on_ground: self.player.speed_y = JUMP_SPEED ## self.player.jumping = True elif self.player.wallgrab: if leftgrab: self.player.speed_y = JUMP_SPEED self.player.speed_x = WALL_JUMP_SPEED self.player.jumping = True ## self.player.air_control_disabled = WALL_JUMP_AIR_CONTROL_BLOCKED elif rightgrab: self.player.speed_y = JUMP_SPEED self.player.speed_x = -WALL_JUMP_SPEED self.player.jumping = True ## self.player.air_control_disabled = WALL_JUMP_AIR_CONTROL_BLOCKED else: self.player.ignore_jump = False if player_on_ground or not self.player.air_control_disabled: if m_left: self.player.speed_x = max(-RUN_SPEED,self.player.speed_x - RUN_ACCEL) if self.player.dash: self.player.speed_x *= 1.5 self.player.SetAction("dash",False) else: self.player.SetAction("walk",False) elif m_right: self.player.speed_x = min(RUN_SPEED,self.player.speed_x + RUN_ACCEL) if self.player.dash: self.player.speed_x *= 1.5 self.player.SetAction("dash",True) else: self.player.SetAction("walk",True) else: if player_on_ground: ## Friction self.player.speed_x *= TERRAINS[ground]["slipping"] if abs(self.player.speed_x) < MIN_SPEED: self.player.speed_x = 0.0 else: self.player.speed_x *= .9 if abs(self.player.speed_x) < MIN_SPEED: self.player.speed_x = 0.0 if not player_on_ground: if m_grab: ## Glide self.player.speed_y = min(TERMINAL_VELOCITY_GLIDING,self.player.speed_y) ## Count down air-control blockage. if self.player.air_control_disabled: self.player.air_control_disabled -= 1 ## Set the player's coordinates now. ## print "Old & new coords: "+str(self.player.coords)+", "+str((new_x,new_y)) ## print "Velocity Y: "+str(self.player.speed_y) self.player.coords = (new_x,new_y) def Go(self): music_available = os.listdir("music") if music_available: StartSong(music_available[0]) while not self.done: self.Logic() self.Draw() self.frames += 1 time_elapsed = time.time() - self.start_time print "FPS: "+str(self.frames / time_elapsed) ##### Other Functions ##### def LoadSprite(sprite_sheet_filename,framesize,rows,cols,colorkey=(255,0,255)): spritesheet = pygame.image.load(os.path.join("graphics",sprite_sheet_filename)).convert() frames = [] for y in range(cols): for x in range(rows): frame = pygame.surface.Surface(framesize) frame.blit(spritesheet,(0,0),(x*framesize[0],y*framesize[1],framesize[0],framesize[1])) frame.set_colorkey(colorkey) frames.append(frame) return frames def StartSong(song_name): pygame.mixer.init() if not "." in song_name: song_name += ".ogg" path = os.path.join("music",song_name) try: pygame.mixer.music.load(path) pygame.mixer.music.play() except: print "Music not found." def LoopSong(): pygame.mixer.music.play() ##### Autorun ##### g = Game() g.Go()