Commits

Flaise  committed c1f14e5

Changed blocks and tiles from tuples to classes. Performance improved slightly
because the shadows now have their surfaces converted for blitting.

  • Participants
  • Parent commits a268702

Comments (0)

Files changed (1)

File squishthebugs/main.py

 '''
 Created: Aug 4, 2013
-Last modified: Aug 13, 2013
+Last modified: Aug 14, 2013
 @author Flaise Saffron
 '''
 
 
 pygame.init()
 surface_screen = pygame.display.set_mode((1024, 768))
-pygame.display.set_caption('Squish the Bugs v0.1')
+pygame.display.set_caption('Squish the Bugs v0.1.2 by Flaise Saffron')
 
 joysticks = [pygame.joystick.Joystick(x) for x in range(pygame.joystick.get_count())]
 for joy in joysticks:
 
 previous_hat = (0, 0)
 def hat_to_direction4(event):
-	global camera, previous_hat
+	global previous_hat
 	if event.value[0] != previous_hat[0]:
 		if event.value[0] == 1:
 			proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION, direction=Direction.EAST))
 TILE_L = .8
 TILE_H = .4
 
-### tile: (surface, (reg_x, reg_y), reg_width, is_filler, relative_layer, hud)
-tile_gray = (pygame.image.load(os.path.join('images', 'Plain Block.png')).convert_alpha(), (50, 90), 100, True, 0, False)
-tile_dirt = (pygame.image.load(os.path.join('images', 'Dirt Block.png')).convert_alpha(), (50, 90), 100, True, 0, False)
-tile_grass = (pygame.image.load(os.path.join('images', 'Grass Block.png')).convert_alpha(), (50, 90), 100, True, 0, False)
-tile_water = (pygame.image.load(os.path.join('images', 'Water Block.png')).convert_alpha(), (50, 90), 100, True, -1, False)
-tile_rock = (pygame.image.load(os.path.join('images', 'Rock.png')).convert_alpha(), (50, 90), 100, False, 0, False)
-tile_tree = (pygame.image.load(os.path.join('images', 'Tree Tall.png')).convert_alpha(), (50, 90), 100, False, 0, False)
-tile_player = (pygame.image.load(os.path.join('images', 'Character Cat Girl.png')).convert_alpha(), (50, 90), 100, False, 0, False)
-tile_player_in_water = (pygame.image.load(os.path.join('images', 'Character Cat Girl In Water.png')).convert_alpha(), (50, 90), 100, False, 0, False)
-tile_bug = (pygame.image.load(os.path.join('images', 'Enemy Bug.png')).convert_alpha(), (50, 90), 100, False, 0, False)
-tile_bug_dead = (pygame.image.load(os.path.join('images', 'Enemy Bug Squished.png')).convert_alpha(), (50, 90), 100, False, -1, False)
-tile_speech = (pygame.image.load(os.path.join('images', 'SpeechBubble.png')).convert_alpha(), (-35, 190), 100, False, 0, True)
-tile_wood = (pygame.image.load(os.path.join('images', 'Wood Block.png')).convert_alpha(), (50, 90), 100, True, 0, False)
-tile_question = (pygame.image.load(os.path.join('images', 'Question.png')).convert_alpha(), (-35, 190), 100, False, 1, True) ### Uses font Palatino Linotype
-tile_bush_0 = (pygame.image.load(os.path.join('images', 'Tree Short.png')).convert_alpha(), (50, 90), 100, False, 0, False)
-tile_bush_1 = (pygame.image.load(os.path.join('images', 'Tree Ugly.png')).convert_alpha(), (50, 90), 100, False, 0, False)
+class Tile:
+	def __init__(self, surface, reg_position, reg_width, is_filler, relative_layer, is_hud):
+		self.surface = surface
+		self.reg_position = reg_position
+		self.reg_width = reg_width
+		self.is_filler = is_filler
+		self.relative_layer = relative_layer
+		self.is_hud = is_hud
 
-shadow_e = (pygame.image.load(os.path.join('images', 'Shadow East.png')), (50, 90), 100)
-shadow_w = (pygame.image.load(os.path.join('images', 'Shadow West.png')), (50, 90), 100)
-shadow_s = (pygame.image.load(os.path.join('images', 'Shadow South.png')), (50, 90), 100)
-shadow_n = (pygame.image.load(os.path.join('images', 'Shadow North.png')), (50, 90), 100)
-shadow_side_w = (pygame.image.load(os.path.join('images', 'Shadow Side West.png')), (50, 90), 100)
-shadow_se = (pygame.image.load(os.path.join('images', 'Shadow South East.png')), (50, 90), 100)
-shadow_nw = (pygame.image.load(os.path.join('images', 'Shadow North West.png')), (50, 90), 100)
-shadow_sw = (pygame.image.load(os.path.join('images', 'Shadow South West.png')), (50, 90), 100)
-shadow_ne = (pygame.image.load(os.path.join('images', 'Shadow North East.png')), (50, 90), 100)
+image_directory = 'images'
+def load_tile(file_name, reg_position, reg_width, is_filler=False, relative_layer=0, is_hud=False):
+	return Tile(
+		pygame.image.load(os.path.join(image_directory, file_name)).convert_alpha(),
+		reg_position, reg_width, is_filler, relative_layer, is_hud
+	)
 
+tile_gray = load_tile('Plain Block.png', (50, 90), 100, True, 0, False)
+tile_dirt = load_tile('Dirt Block.png', (50, 90), 100, True, 0, False)
+tile_grass = load_tile('Grass Block.png', (50, 90), 100, True, 0, False)
+tile_water = load_tile('Water Block.png', (50, 90), 100, True, -1, False)
+tile_rock = load_tile('Rock.png', (50, 90), 100, False, 0, False)
+tile_tree = load_tile('Tree Tall.png', (50, 90), 100, False, 0, False)
+tile_player = load_tile('Character Cat Girl.png', (50, 90), 100, False, 0, False)
+tile_player_in_water = load_tile('Character Cat Girl In Water.png', (50, 90), 100, False, 0, False)
+tile_bug = load_tile('Enemy Bug.png', (50, 90), 100, False, 0, False)
+tile_bug_dead = load_tile('Enemy Bug Squished.png', (50, 90), 100, False, -1, False)
+tile_speech = load_tile('SpeechBubble.png', (-35, 190), 100, False, 0, True)
+tile_wood = load_tile('Wood Block.png', (50, 90), 100, True, 0, False)
+tile_question = load_tile('Question.png', (-35, 190), 100, False, 1, True) ### Uses font Palatino Linotype
+tile_bush_0 = load_tile('Tree Short.png', (50, 90), 100, False, 0, False)
+tile_bush_1 = load_tile('Tree Ugly.png', (50, 90), 100, False, 0, False)
+
+shadow_e = load_tile('Shadow East.png', (50, 90), 100)
+shadow_w = load_tile('Shadow West.png', (50, 90), 100)
+shadow_s = load_tile('Shadow South.png', (50, 90), 100)
+shadow_n = load_tile('Shadow North.png', (50, 90), 100)
+shadow_side_w = load_tile('Shadow Side West.png', (50, 90), 100)
+shadow_se = load_tile('Shadow South East.png', (50, 90), 100)
+shadow_nw = load_tile('Shadow North West.png', (50, 90), 100)
+shadow_sw = load_tile('Shadow South West.png', (50, 90), 100)
+shadow_ne = load_tile('Shadow North East.png', (50, 90), 100)
+
+
+
+blocks = []
+position_to_blocklist = {} ### contains: {(x, y, z): [blocks]}
 
 class BlockType:
 	PLAYER, BUG, ROCK, TREE, FILLER, BUG_DEAD, SPEECH, WATER = range(8)
-
-blocks = [] ### contains: ((x, y, z), tile, BlockType)
-position_to_blocklist = {} ### contains: {(x, y, z): [blocks]}
+class Block:
+	def __init__(self, position, tile, block_type):
+		self.position = position
+		self.tile = tile
+		self.block_type = block_type
+		
+		blocks.append(self)
+		update_blocks()
+		
+	def move(self, delta):
+		self.move_to(sum3d(self.position, delta))
+	
+	def move_to(self, dest):
+		self.position = dest
+		update_blocks()
+		proc_event(pygame.event.Event(AppEvent.BLOCK_MOVED, block=self, dest=dest))
+	
+	def remove(self):
+		position_to_blocklist[self.position].remove(self)
+		if len(position_to_blocklist[self.position]) == 0:
+			del position_to_blocklist[self.position]
+		blocks.remove(self)
+		proc_event(pygame.event.Event(AppEvent.BLOCK_MOVED, block=self, dest=None))
+		
 
 
 def clear_blocks():
 on_event(lambda a: a.type == AppEvent.RESTART, lambda a: clear_blocks())
 
 def update_blocks():
-	blocks.sort(key=lambda a: a[0][2] * 10000 + a[0][1] + a[1][4] + (100000000 if a[1][5] else 0))
+	blocks.sort(key=lambda a: a.position[2] * 10000 + a.position[1] + a.tile.relative_layer + (100000000 if a.tile.is_hud else 0))
 	position_to_blocklist.clear()
 	for block in blocks:
-		position = block[0]
+		position = block.position
 		if position not in position_to_blocklist:
 			position_to_blocklist[position] = []
 		position_to_blocklist[position].append(block)
-
-def remove_block(block):
-	position_to_blocklist[block[0]].remove(block)
-	if len(position_to_blocklist[block[0]]) == 0:
-		del position_to_blocklist[block[0]]
-	blocks.remove(block)
-	proc_event(pygame.event.Event(AppEvent.BLOCK_MOVED, block=block, dest=None))
 	
 def is_block_at(position):
 	return position in position_to_blocklist and len(position_to_blocklist[position]) > 0
 
-def move_block_to(block, dest):
-	block[0] = dest
-	update_blocks()
-	proc_event(pygame.event.Event(AppEvent.BLOCK_MOVED, block=block, dest=dest))
-	
-def move_block(block, delta):
-	move_block_to(block, sum3d(block[0], delta))
-
 def get_block_types_at(position):
 	for block in get_blocks_at(position):
-		yield block[2]
+		yield block.block_type
 		
 def get_blocks_at(position):
 	if position in position_to_blocklist:
 def is_block_type_at(position, block_type):
 	return block_type in get_block_types_at(position)
 
-def make_block(position, tile, block_type):
-	if len(position) != 3: raise Exception()
-	block = [position, tile, block_type]
-	blocks.append(block)
-	update_blocks()
-	return block
 
 
 
 		return len([x for x in get_block_types_at(position) if is_obstacle_type(x)]) > 0
 	
 	def on_fail():
-		bubble = make_block(block[0], tile_speech, BlockType.SPEECH)
-		question = make_block(block[0], tile_question, BlockType.SPEECH)
+		bubble = Block(block.position, tile_speech, BlockType.SPEECH)
+		question = Block(block.position, tile_question, BlockType.SPEECH)
 		
 		def remove(event):
 			if event.type == AppEvent.RESTART:
 				return
-			remove_block(bubble)
-			remove_block(question)
+			bubble.remove()
+			question.remove()
 		on_event_once(lambda a: a.type == AppEvent.INPUT_DIRECTION or a.type == AppEvent.RESTART, remove)
 	
-	dest = sum3d(block[0], delta)
+	dest = sum3d(block.position, delta)
 	above = sum3d(dest, (0, 1, 0))
 	
 	if is_obstacle_at(dest):
 			return
 		for block_type in get_block_types_at(dest):
 			if block_type == BlockType.FILLER:
-				move_block(block, sum3d(delta, (0, 1, 0)))
+				block.move(sum3d(delta, (0, 1, 0)))
 				return
 		on_fail()
 		return
 			below = sum3d(dest, (0, -i, 0))
 			
 			for other_block in get_blocks_at(below):
-				block_type = other_block[2]
-				
-				if block_type in (BlockType.FILLER, BlockType.ROCK):
-					move_block(block, sum3d(delta, (0, 1 - i, 0)))
+				if other_block.block_type in (BlockType.FILLER, BlockType.ROCK):
+					block.move(sum3d(delta, (0, 1 - i, 0)))
 					return
-				elif block_type == BlockType.BUG:
+				elif other_block.block_type == BlockType.BUG:
 					squish_bug(other_block)
-					move_block(block, sum3d(delta, (0, -i, 0)))
+					block.move(sum3d(delta, (0, -i, 0)))
 					return
-				elif is_obstacle_type(block_type):
+				elif is_obstacle_type(other_block.block_type):
 					on_fail()
 					return
 				else:
 	
 def make_player(position):
 	global block_player
-	block_player = make_block(position, tile_player, BlockType.PLAYER)
+	block_player = Block(position, tile_player, BlockType.PLAYER)
 	
 	def center_camera_on_player():
-		move_camera_to(sum3d(block_player[0], (0, -1, 0)))
+		move_camera_to(sum3d(block_player.position, (0, -1, 0)))
 	center_camera_on_player()
 	
 	def update_tile():
-		if is_block_type_at(block_player[0], BlockType.WATER):
-			block_player[1] = tile_player_in_water
+		if is_block_type_at(block_player.position, BlockType.WATER):
+			block_player.tile = tile_player_in_water
 		else:
-			block_player[1] = tile_player
+			block_player.tile = tile_player
 	update_tile()
 	
 	
 	global num_bugs
 	num_bugs -= 1
 	
-	block[1] = tile_bug_dead
-	block[2] = BlockType.BUG_DEAD
+	block.tile = tile_bug_dead
+	block.block_type = BlockType.BUG_DEAD
 	
 	proc_event(pygame.event.Event(AppEvent.BUG_SQUISHED, block=block))
 
 def can_bug_move(block, delta):
-	if len(list(get_blocks_at(sum3d(block[0], (0, 1, 0))))) > 0: # something on top, usually another bug
+	if len(list(get_blocks_at(sum3d(block.position, (0, 1, 0))))) > 0: # something on top, usually another bug
 		return False # let other bug move off first so odd behavior doesn't commence
 	
 	def is_obstacle(block_type):
 	def is_obstacle_at(position):
 		return len([x for x in get_block_types_at(position) if is_obstacle(x)]) > 0
 	
-	dest = sum3d(block[0], delta)
+	dest = sum3d(block.position, delta)
 	
 	if is_obstacle_at(dest):
 		return False
 		below = sum3d(dest, (0, -1, 0))
 		
 		for other_block in get_blocks_at(below):
-			block_type = other_block[2]
-			
-			if block_type in (BlockType.FILLER, BlockType.ROCK, BlockType.BUG):
+			if other_block.block_type in (BlockType.FILLER, BlockType.ROCK, BlockType.BUG):
 				return True
-			elif not is_obstacle(block_type):
+			elif not is_obstacle(other_block.block_type):
 				continue
 			return False
 		
 def move_bug(block, delta):
 	if can_bug_move(block, delta):
-		move_block(block, delta)
+		block.move(delta)
 		
 def make_bug(position):
 	global num_bugs
 	def do_ai(event):
 		nonlocal last_move
 		
-		if block[2] == BlockType.BUG_DEAD: return
+		if block.block_type == BlockType.BUG_DEAD: return
 		
 		deltas = [(1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)]
 		
 				move_bug(block, reverse_move)
 				last_move = reverse_move
 	
-	block = make_block(position, tile_bug, BlockType.BUG)
+	block = Block(position, tile_bug, BlockType.BUG)
 	on_event(lambda a: a.type == AppEvent.BLOCK_MOVED and a.block == block_player, do_ai)
 	### TODO: Remove event binding when bug-death event fires
 	
 
 
 def make_grass(position):
-	make_block(position, tile_grass, BlockType.FILLER)
+	Block(position, tile_grass, BlockType.FILLER)
 def make_stone(position):
-	make_block(position, tile_gray, BlockType.FILLER)
+	Block(position, tile_gray, BlockType.FILLER)
 def make_boulder(position):
-	make_block(position, tile_rock, BlockType.ROCK)
+	Block(position, tile_rock, BlockType.ROCK)
 def make_tree(position):
-	make_block(position, tile_tree, BlockType.TREE)
+	Block(position, tile_tree, BlockType.TREE)
 def make_bush_0(position):
-	make_block(position, tile_bush_0, BlockType.TREE)
+	Block(position, tile_bush_0, BlockType.TREE)
 def make_bush_1(position):
-	make_block(position, tile_bush_1, BlockType.TREE)
+	Block(position, tile_bush_1, BlockType.TREE)
 def make_wood_floor(position):
-	make_block(position, tile_wood, BlockType.FILLER)
+	Block(position, tile_wood, BlockType.FILLER)
 def make_water(position):
-	make_block(position, tile_water, BlockType.WATER)
+	Block(position, tile_water, BlockType.WATER)
 def make_dirt(position):
-	make_block(position, tile_dirt, BlockType.FILLER)
+	Block(position, tile_dirt, BlockType.FILLER)
 
 
 scale_cache = {} ### { surface: ((x, y), result_surface) }
 	return scale_cache[surface][1]
 
 def draw_tile(tile, position):
-	tile_surface = tile[0]
-	tile_reg_width = tile[2]
-	
 	scale_world2screen = surface_screen.get_height() / (camera[1] * TILE_L)
-	scale_image2tile = tile_surface.get_width() / tile_reg_width
+	scale_image2tile = tile.surface.get_width() / tile.reg_width
 	
 	dest_w = scale_world2screen * scale_image2tile
-	dest_h = dest_w * tile_surface.get_height() / tile_surface.get_width()
+	dest_h = dest_w * tile.surface.get_height() / tile.surface.get_width()
 	surface_screen.blit(
-		scale_surface_to(tile_surface, int2d((dest_w, dest_h))),
+		scale_surface_to(tile.surface, int2d((dest_w, dest_h))),
 		sum2d(
 			### positioning before camera motion
 			dif2d(
 					),
 					scale_world2screen
 				),
-				scale2d(tile[1], scale_world2screen / tile_reg_width)
+				scale2d(tile.reg_position, scale_world2screen / tile.reg_width)
 			),
 			
 			sum2d(
 def draw_environment(event):
 	def is_filler_at(position):
 		for block in get_blocks_at(position):
-			if block[1][3]:
+			if block.tile.is_filler:
 				return True
 		return False
 	
 	for block in blocks:
-		tile = block[1]
-		position = block[0]
-		draw_tile(tile, position)
-		if tile[3]:
+		draw_tile(block.tile, block.position)
+		if block.tile.is_filler:
 			### surface shadows
-			if not is_filler_at(sum3d(position, (0, 1, 0))):
-				if is_filler_at(sum3d(position, (1, 1, 0))):
-					draw_tile(shadow_e, position)
-				if is_filler_at(sum3d(position, (-1, 1, 0))):
-					draw_tile(shadow_w, position)
-				if is_filler_at(sum3d(position, (0, 1, 1))):
-					draw_tile(shadow_s, position)
-				if is_filler_at(sum3d(position, (0, 1, -1))):
-					draw_tile(shadow_n, position)
+			if not is_filler_at(sum3d(block.position, (0, 1, 0))):
+				if is_filler_at(sum3d(block.position, (1, 1, 0))):
+					draw_tile(shadow_e, block.position)
+				if is_filler_at(sum3d(block.position, (-1, 1, 0))):
+					draw_tile(shadow_w, block.position)
+				if is_filler_at(sum3d(block.position, (0, 1, 1))):
+					draw_tile(shadow_s, block.position)
+				if is_filler_at(sum3d(block.position, (0, 1, -1))):
+					draw_tile(shadow_n, block.position)
 				
 				### corners
-				if not is_filler_at(sum3d(position, (0, 1, 1))):
-					if is_filler_at(sum3d(position, (1, 1, 1))) and not is_filler_at(sum3d(position, (1, 1, 0))):
-						draw_tile(shadow_se, position)
-					if is_filler_at(sum3d(position, (-1, 1, 1))) and not is_filler_at(sum3d(position, (-1, 1, 0))):
-						draw_tile(shadow_sw, position)
-				if is_filler_at(sum3d(position, (-1, 1, -1))) and not is_filler_at(sum3d(position, (0, 1, -1))) and not is_filler_at(sum3d(position, (-1, 1, 0))):
-					draw_tile(shadow_nw, position)
-				if is_filler_at(sum3d(position, (1, 1, -1))) and not is_filler_at(sum3d(position, (0, 1, -1))) and not is_filler_at(sum3d(position, (1, 1, 0))):
-					draw_tile(shadow_ne, position)
+				if not is_filler_at(sum3d(block.position, (0, 1, 1))):
+					if is_filler_at(sum3d(block.position, (1, 1, 1))) and not is_filler_at(sum3d(block.position, (1, 1, 0))):
+						draw_tile(shadow_se, block.position)
+					if is_filler_at(sum3d(block.position, (-1, 1, 1))) and not is_filler_at(sum3d(block.position, (-1, 1, 0))):
+						draw_tile(shadow_sw, block.position)
+				if is_filler_at(sum3d(block.position, (-1, 1, -1))) and not is_filler_at(sum3d(block.position, (0, 1, -1))) and not is_filler_at(sum3d(block.position, (-1, 1, 0))):
+					draw_tile(shadow_nw, block.position)
+				if is_filler_at(sum3d(block.position, (1, 1, -1))) and not is_filler_at(sum3d(block.position, (0, 1, -1))) and not is_filler_at(sum3d(block.position, (1, 1, 0))):
+					draw_tile(shadow_ne, block.position)
 			
 			### front shadows
-			if not is_filler_at(sum3d(position, (0, 0, 1))):
-				if is_filler_at(sum3d(position, (-1, 0, 1))):
-					draw_tile(shadow_side_w, position)
+			if not is_filler_at(sum3d(block.position, (0, 0, 1))):
+				if is_filler_at(sum3d(block.position, (-1, 0, 1))):
+					draw_tile(shadow_side_w, block.position)
 					
 on_event(lambda a: a.type == EVENT_DRAW, draw_environment)
 
 
 
 
-#################################################################### unit tests
-
-assert mag_scale_to_2d((1, 0), 2) == (2, 0)
-assert mag_scale_to_2d((-2, 0), .5) == (-.5, 0)
-assert mag_scale_to_2d((1, 1), 1) == (1 / math.sqrt(2), 1 / math.sqrt(2))
-
-
-
 ######################################################################### begin
 
 ### obviously not the best map loading solution in a large app, but whatever