Commits

Flaise  committed 0659ba3

Added bug squishing, event removal, and a visual for failed player movements,
and improved the AI to make a bug always pick a valid move when possible.

  • Participants
  • Parent commits 991df99

Comments (0)

Files changed (6)

File roguelike/Dirt Block.png

Added
New image

File roguelike/Enemy Bug Squished.png

Added
New image

File roguelike/Question.png

Added
New image

File roguelike/Shadow North Clipped.png

Added
New image

File roguelike/SpeechBubble.png

Added
New image

File roguelike/main.py

 '''
 Created: Aug 4, 2013
-Last modified: Aug 8, 2013 
+Last modified: Aug 10, 2013 
 '''
 
 import pygame
 EVENT_DRAW = 100
 
 event_handlers = [] # contains: (predicate:function, callback:function)
+next_event_handlers = []
 def proc_event(event):
+	global event_handlers, next_event_handlers
+	
+	event_handlers = next_event_handlers
+	next_event_handlers = list(event_handlers)
 	for handler in event_handlers:
+		#if handler == None: continue
+		
 		if handler[0](event):
 			handler[1](event)
 			
 	### registered in any order
 	
 	### TODO: what about attaching events to other events?
+	
+	removed = False
+	def invoke(event):
+		if removed: return
+		
+		callback(event)
+	
 	def remove():
-		raise Exception('on_event()~~>remove() not implemented')
+		nonlocal removed
+		
+		if removed: return
+		removed = True
+		#raise Exception('on_event()~~>remove() not implemented')
 		### This requires a linked list or something similar
 		
-	event_handlers.append((predicate, callback))
+		#event_handlers[index] = None
+		next_event_handlers.remove(handler)
+	
+	handler = (predicate, invoke)
+	#index = len(event_handlers)
+	next_event_handlers.append(handler)
 	return remove
 
+def on_event_once(predicate, callback):
+	stop = on_event(predicate, lambda a: (callback(a), stop()))
+
 
 
 
 
 ########################################################################### app
 
-EVENT_IN_NORTH = 1000
-EVENT_IN_EAST = 1001
-EVENT_IN_SOUTH = 1002
-EVENT_IN_WEST = 1003
-on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_UP, lambda a: proc_event(pygame.event.Event(EVENT_IN_NORTH)))
-on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_DOWN, lambda a: proc_event(pygame.event.Event(EVENT_IN_SOUTH)))
-on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_RIGHT, lambda a: proc_event(pygame.event.Event(EVENT_IN_EAST)))
-on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_LEFT, lambda a: proc_event(pygame.event.Event(EVENT_IN_WEST)))
-
-EVENT_PLAYER_MOVED = 1004
-
-previous_hat = (0, 0)
-def hat_to_direction4(event):
-	global camera, previous_hat
-	if event.value[0] != previous_hat[0]:
-		if event.value[0] == 1:
-			proc_event(pygame.event.Event(EVENT_IN_EAST))
-		elif event.value[0] == -1:
-			proc_event(pygame.event.Event(EVENT_IN_WEST))
-	if event.value[1] != previous_hat[1]:
-		if event.value[1] == 1:
-			proc_event(pygame.event.Event(EVENT_IN_NORTH))
-		elif event.value[1] == -1:
-			proc_event(pygame.event.Event(EVENT_IN_SOUTH))
-	previous_hat = event.value
-on_event(lambda a: a.type == pygame.JOYHATMOTION, lambda a: hat_to_direction4(a))
-
 pygame.init()
-#pygame.mouse.set_cursor(pygame.cursors.)
-pygame.mouse.set_visible(False)
+#pygame.mouse.set_visible(False)
 surface_screen = pygame.display.set_mode(
 	#(0, 0),
 	#(600, 400),
 	#| pygame.DOUBLEBUF
 	#| pygame.HWSURFACE
 )
-pygame.display.set_caption('Roguelike v0.0.3')
+pygame.display.set_caption('Roguelike v0.0.4')
 
 
+class AppEvent:
+	#NORTH, EAST, SOUTH, WEST,
+	INPUT_DIRECTION, BLOCK_MOVED = range(1000, 1002)
+class Direction:
+	NORTH, EAST, SOUTH, WEST = range(4)
+
+#on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_UP, lambda a: proc_event(pygame.event.Event(AppEvent.NORTH)))
+#on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_DOWN, lambda a: proc_event(pygame.event.Event(AppEvent.SOUTH)))
+#on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_RIGHT, lambda a: proc_event(pygame.event.Event(AppEvent.EAST)))
+#on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_LEFT, lambda a: proc_event(pygame.event.Event(AppEvent.WEST)))
+
+on_event(
+	lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_UP,
+	lambda a: proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION, direction=Direction.NORTH))
+)
+on_event(
+	lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_DOWN,
+	lambda a: proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION, direction=Direction.SOUTH))
+)
+on_event(
+	lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_RIGHT,
+	lambda a: proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION, direction=Direction.EAST))
+)
+on_event(
+	lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_LEFT,
+	lambda a: proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION, direction=Direction.WEST))
+)
+
+previous_hat = (0, 0)
+def hat_to_direction4(event):
+	global camera, previous_hat
+	if event.value[0] != previous_hat[0]:
+		if event.value[0] == 1:
+			#proc_event(pygame.event.Event(AppEvent.EAST))
+			proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION))
+		elif event.value[0] == -1:
+			#proc_event(pygame.event.Event(AppEvent.WEST))
+			proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION))
+	if event.value[1] != previous_hat[1]:
+		if event.value[1] == 1:
+			#proc_event(pygame.event.Event(AppEvent.NORTH))
+			proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION))
+		elif event.value[1] == -1:
+			#proc_event(pygame.event.Event(AppEvent.SOUTH))
+			proc_event(pygame.event.Event(AppEvent.INPUT_DIRECTION))
+	previous_hat = event.value
+on_event(lambda a: a.type == pygame.JOYHATMOTION, lambda a: hat_to_direction4(a))
 
 
 on_event(lambda a: a.type == EVENT_DRAW, lambda a: surface_screen.fill(colorBG))
 def zoom_camera(delta):
 	global camera
 	camera = (camera[0], camera[1], max(1, camera[2] + delta))
+
+
+blocks = [] # contains: ((x, y, z), tile)
+position_to_blocklist = {}
+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))
+	position_to_blocklist.clear()
+	for block in blocks:
+		#position_to_blocklist[block[0]] = block
+		if block[0] not in position_to_blocklist:
+			position_to_blocklist[block[0]] = []
+		position_to_blocklist[block[0]].append(block)
+
+"""def remove_block_at(position):
+	global blocks
+	for i, block in enumerate(blocks):
+		if block[0] == position:
+			blocks = blocks[:i] + blocks[i + 1:]
+			break
+	del position_to_blocklist[position]
+	#blocks.remove(value)
+"""
+def remove_block(block):
+	position_to_blocklist[block[0]].remove(block)
+	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]
+def get_blocks_at(position):
+	if position in position_to_blocklist:
+		#yield from position_to_blocklist[position]
+		for block in position_to_blocklist[position]:
+			yield block
+	
+def move_player(block, delta):
+	def is_obstacle_type(block_type):
+		return block_type in (BlockType.BUG, BlockType.ROCK, BlockType.TREE, BlockType.FILLER)
+	def is_obstacle_at(position):
+		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)
+		on_event_once(lambda a: a.type == AppEvent.INPUT_DIRECTION, lambda a: remove_block(bubble))
+		
+		question = make_block(block[0], tile_question, BlockType.SPEECH)
+		on_event_once(lambda a: a.type == AppEvent.INPUT_DIRECTION, lambda a: remove_block(question))
+		#stop = on_event(
+		#	lambda a: a.type == AppEvent.BLOCK_MOVED and a.block == block,
+		#	lambda a: ( remove_block(bubble), stop() )
+		#)
+	
+	dest = sum3d(block[0], delta)
+	above = sum3d(dest, (0, 1, 0))
+	
+	if is_obstacle_at(dest):
+		if is_obstacle_at(above):
+			on_fail()
+			return
+		for block_type in get_block_types_at(dest):
+			if block_type == BlockType.FILLER:
+				move_block(block, sum3d(delta, (0, 1, 0)))
+				return
+		on_fail()
+		return
+	else:
+		for i in range(1, 5):
+			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)))
+					return
+				elif block_type == BlockType.BUG:
+					other_block[1] = tile_bug_dead
+					other_block[2] = BlockType.BUG_DEAD
+					move_block(block, sum3d(delta, (0, -i, 0)))
+					return
+				elif is_obstacle_type(block_type):
+					on_fail()
+					return
+				else:
+					continue
+		on_fail()
+
+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
+		return False # let it move off first so odd behavior doesn't commence
+	
+	def is_obstacle(block_type):
+		return block_type in (BlockType.PLAYER, BlockType.BUG, BlockType.ROCK, BlockType.TREE, BlockType.FILLER)
+	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)
+	
+	if is_obstacle_at(dest):
+		return False
+	else:
+		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):
+				return True
+			elif not is_obstacle(block_type):
+				continue
+			return False
+	
+def move_bug(block, delta):
+	"""def is_obstacle_type(block_type):
+		return block_type in (BlockType.PLAYER, BlockType.BUG, BlockType.ROCK, BlockType.TREE, BlockType.FILLER)
+	def is_obstacle_at(position):
+		return len([x for x in get_block_types_at(position) if is_obstacle_type(x)]) > 0
+	
+	dest = sum3d(block[0], delta)
+	
+	if is_obstacle_at(dest):
+		return
+	else:
+		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):
+				move_block(block, delta)
+			elif not is_obstacle_type(block_type):
+				continue
+			return"""
+	if can_bug_move(block, delta):
+		move_block(block, delta)
+
+def ai_bug(block):
+	if block[2] == BlockType.BUG_DEAD: return
+	#move_bug(block, random.choice(((1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1))))
+	
+	options = [x for x in ((1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)) if can_bug_move(block, x)]
+	if len(options) > 0:
+		move_bug(block, random.choice(options))
+
+
+def center_camera_on_block(block):
+	move_camera_to((block[0][0], block[0][2]))
+on_event(lambda a: a.type == AppEvent.BLOCK_MOVED and a.block == block_player, lambda a: center_camera_on_block(block_player))
+
+
 TILE_W = 1
 TILE_L = .8
 TILE_H = .4
 
-
-# tile: (surface, (reg_x, reg_y), reg_width, is_filler)        , can_stand_on, can_climb)
-tile_gray = (pygame.image.load('Plain Block.png').convert_alpha(), (50, 90), 100, True)#, True, True)
-tile_dirt = (pygame.image.load('Brown Block.png').convert_alpha(), (50, 90), 100, True)#, True, True)
-tile_grass = (pygame.image.load('Grass Block.png').convert_alpha(), (50, 90), 100, True)#, True, True)
-tile_water = (pygame.image.load('Water Block.png').convert_alpha(), (50, 90), 100, True)#, True, True)
-tile_rock = (pygame.image.load('Rock.png').convert_alpha(), (50, 90), 100, False)#, True, False)
-tile_tree = (pygame.image.load('Tree Tall.png').convert_alpha(), (50, 90), 100, False)#, False, False)
-tile_player = (pygame.image.load('Character Cat Girl.png').convert_alpha(), (50, 90), 100, False)#, False, False)
-tile_bug = (pygame.image.load('Enemy Bug.png').convert_alpha(), (50, 90), 100, False)#, False, False)
+# tile: (surface, (reg_x, reg_y), reg_width, is_filler, relative_layer, hud)
+tile_gray = (pygame.image.load('Plain Block.png').convert_alpha(), (50, 90), 100, True, 0, False)
+tile_dirt = (pygame.image.load('Dirt Block.png').convert_alpha(), (50, 90), 100, True, 0, False)
+tile_grass = (pygame.image.load('Grass Block.png').convert_alpha(), (50, 90), 100, True, 0, False)
+tile_water = (pygame.image.load('Water Block.png').convert_alpha(), (50, 90), 100, True, 0, False)
+tile_rock = (pygame.image.load('Rock.png').convert_alpha(), (50, 90), 100, False, 0, False)
+tile_tree = (pygame.image.load('Tree Tall.png').convert_alpha(), (50, 90), 100, False, 0, False)
+tile_player = (pygame.image.load('Character Cat Girl.png').convert_alpha(), (50, 90), 100, False, 0, False)
+tile_bug = (pygame.image.load('Enemy Bug.png').convert_alpha(), (50, 90), 100, False, 0, False)
+tile_bug_dead = (pygame.image.load('Enemy Bug Squished.png').convert_alpha(), (50, 90), 100, False, -1, False)
+tile_speech = (pygame.image.load('SpeechBubble.png').convert_alpha(), (-35, 190), 100, False, 0, True)
+tile_wood = (pygame.image.load('Wood Block.png').convert_alpha(), (50, 90), 100, True, 0, False)
+tile_question = (pygame.image.load('Question.png').convert_alpha(), (-35, 190), 100, False, 1, True)
 
 shadow_e = (pygame.image.load('Shadow East.png'), (50, 90), 100)
 shadow_w = (pygame.image.load('Shadow West.png'), (50, 90), 100)
 shadow_s = (pygame.image.load('Shadow South.png'), (50, 90), 100)
-shadow_n = (pygame.image.load('Shadow North.png'), (50, 90), 100)
+shadow_n = (pygame.image.load('Shadow North Clipped.png'), (50, 90), 100)
 shadow_side_w = (pygame.image.load('Shadow Side West.png'), (50, 90), 100)
 shadow_se = (pygame.image.load('Shadow South East.png'), (50, 90), 100)
 shadow_nw = (pygame.image.load('Shadow North West.png'), (50, 90), 100)
 shadow_ne = (pygame.image.load('Shadow North East.png'), (50, 90), 100)
 
 
-#def get_blocks(predicate):
-#	for block in blocks:
-#		if predicate(block):
-#			yield block
-#def get_blocks_at(position):
-#	yield from get_blocks(lambda a: a[0] == position)
+class BlockType:
+	PLAYER, BUG, ROCK, TREE, FILLER, BUG_DEAD, SPEECH = range(7)
 
-
-"""def is_filler_at(position):
-	if is_block_at(position):
-		if position_to_block[position][1][3]:
-			return True
-	return False
-def is_block_at(position):
-	return position in position_to_block
-
-def move_block(block, delta):
-	block[0] = sum3d(block[0], delta)
-	
-	global blocks_changed
-	blocks_changed = True
-
-def can_stand_on(position):
-	if position in position_to_block:
-		if position_to_block[position][1][4]:
-			return True
-	return False
-def can_climb(position):
-	if not position in position_to_block: return False
-	
-	return position_to_block[position][1][5]
-###"""
-
-
-blocks = [] # contains: ((x, y, z), tile)
-position_to_block = {}
-#blocks_changed = True
-def update_blocks():
-	#global blocks_changed
-	blocks.sort(key=lambda a: a[0][2] * 10000000 + a[0][1])
-	position_to_block.clear()
-	for block in blocks:
-		position_to_block[block[0]] = block
-	#blocks_changed = False
-
-def remove_block_at(position):
-	global blocks
-	for i, block in enumerate(blocks):
-		if block[0] == position:
-			blocks = blocks[:i] + blocks[i + 1:]
-			break
-	del position_to_block[position]
-	#blocks.remove(value)
-	
-def is_block_at(position):
-	return position in position_to_block
-
-def move_block_to(block, dest):
-	block[0] = dest
-	
-	#global blocks_changed
-	#blocks_changed = True
+def make_block(position, tile, block_type):
+	block = [position, tile, block_type]
+	blocks.append(block)
 	update_blocks()
-	
-def move_block(block, delta):
-	move_block_to(block, sum3d(block[0], delta))
-
-def get_block_type_at(position):
-	if position in position_to_block:
-		return position_to_block[position][2]
-	else:
-		return None
-	
-def move_player(block, delta):
-	dest = sum3d(block[0], delta)
-	above = sum3d(dest, (0, 1, 0))
-	#below = sum3d(dest, (0, -1, 0))
-	
-	if is_block_at(dest):
-		if is_block_at(above):
-			return
-		elif get_block_type_at(dest) == BlockType.FILLER:
-			move_block(block, sum3d(delta, (0, 1, 0)))
-			
-			proc_event(pygame.event.Event(EVENT_PLAYER_MOVED))
-		return
-	else:
-		for i in range(1, 5):
-			below = sum3d(dest, (0, -i, 0))
-			if get_block_type_at(below) == None:
-				continue
-			if get_block_type_at(below) in (BlockType.FILLER, BlockType.ROCK):
-				move_block(block, sum3d(delta, (0, 1 - i, 0)))
-				proc_event(pygame.event.Event(EVENT_PLAYER_MOVED))
-			if get_block_type_at(below) in (BlockType.BUG,):
-				remove_block_at(below)
-				
-				move_block(block, sum3d(delta, (0, -i, 0)))
-				proc_event(pygame.event.Event(EVENT_PLAYER_MOVED))
-			return
-		
-def move_bug(block, delta):
-	dest = sum3d(block[0], delta)
-	above = sum3d(dest, (0, 1, 0))
-	#below = sum3d(dest, (0, -1, 0))
-	
-	if is_block_at(dest):
-		#if is_block_at(above):
-		#	return
-		#elif get_block_type_at(dest) == BlockType.FILLER:
-		#	move_block(block, sum3d(delta, (0, 1, 0)))
-		#	
-		#	#proc_event(pygame.event.Event(EVENT_PLAYER_MOVED))
-		return
-	else:
-		
-		#for i in range(1, 5):
-		below = sum3d(dest, (0, -1, 0))
-		if get_block_type_at(below) == None:
-			#continue
-			return
-		if get_block_type_at(below) in (BlockType.FILLER, BlockType.ROCK, BlockType.BUG):
-			move_block(block, delta)
-			#proc_event(pygame.event.Event(EVENT_PLAYER_MOVED))
-		return
-
-def ai_bug(block):
-	move_bug(block, random.choice(((1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1))))
-
-on_event(lambda a: a.type == EVENT_PLAYER_MOVED, lambda a: ai_bug(block_bug))
-
-#def move_player_and_camera(block, delta):
-#	if move_player(block, delta):
-#		center_camera_on_block(block)
-def center_camera_on_block(block):
-	move_camera_to((block[0][0], block[0][2]))
-on_event(lambda a: a.type == EVENT_PLAYER_MOVED, lambda a: center_camera_on_block(block_player))
-
-class BlockType:
-	PLAYER, BUG, ROCK, TREE, FILLER = range(5)
+	return block
 
 
 blocks.append([(1, 1, 2), tile_gray, BlockType.FILLER])
 blocks.append([(2, 0, 2), tile_dirt, BlockType.FILLER])
 blocks.append([(2, 1, 1), tile_grass, BlockType.FILLER])
 blocks.append([(2, 1, 2), tile_grass, BlockType.FILLER])
-blocks.append([(1, 0, 3), tile_grass, BlockType.FILLER])
-blocks.append([(1, 0, 4), tile_water, BlockType.FILLER])
-blocks.append([(0, 0, 4), tile_water, BlockType.FILLER])
-blocks.append([(1, 0, 5), tile_dirt, BlockType.FILLER])
-blocks.append([(0, -1, 5), tile_dirt, BlockType.FILLER])
+#blocks.append([(1, 0, 3), tile_grass, BlockType.FILLER])
+#blocks.append([(1, 0, 4), tile_water, BlockType.FILLER])
+#blocks.append([(0, 0, 4), tile_water, BlockType.FILLER])
+#blocks.append([(1, 0, 5), tile_dirt, BlockType.FILLER])
+#blocks.append([(0, -1, 5), tile_dirt, BlockType.FILLER])
 blocks.append([(0, 0, -2), tile_dirt, BlockType.FILLER])
 blocks.append([(0, 0, 2), tile_dirt, BlockType.FILLER])
 blocks.append([(0, 1, 0), tile_rock, BlockType.ROCK])
 blocks.append([(3, 2, 2), tile_grass, BlockType.FILLER])
 blocks.append([(3, 1, 2), tile_dirt, BlockType.FILLER])
 blocks.append([(3, 1, -1), tile_dirt, BlockType.FILLER])
+blocks.append([(1, 0, 3), tile_dirt, BlockType.FILLER])
+blocks.append([(0, 0, 3), tile_dirt, BlockType.FILLER])
+blocks.append([(2, 0, 3), tile_dirt, BlockType.FILLER])
+blocks.append([(-1, 1, -2), tile_wood, BlockType.FILLER])
+blocks.append([(-2, 0, 0), tile_grass, BlockType.FILLER])
+blocks.append([(-2, 1, -1), tile_wood, BlockType.FILLER])
+blocks.append([(-2, 1, -2), tile_wood, BlockType.FILLER])
+blocks.append([(4, 1, 0), tile_dirt, BlockType.FILLER])
+blocks.append([(4, 1, 1), tile_gray, BlockType.FILLER])
+blocks.append([(4, 1, 2), tile_gray, BlockType.FILLER])
 
 block_player = [(1, 1, 0), tile_player, BlockType.PLAYER]
 center_camera_on_block(block_player)
 blocks.append(block_player)
 
-block_bug = [(1, 1, -1), tile_bug, BlockType.BUG]
-blocks.append(block_bug)
+block_bug_0 = [(1, 1, -1), tile_bug, BlockType.BUG]
+blocks.append(block_bug_0)
+on_event(lambda a: a.type == AppEvent.BLOCK_MOVED and a.block == block_player, lambda a: ai_bug(block_bug_0))
+
+block_bug_1 = [(-1, 0, 0), tile_bug, BlockType.BUG]
+blocks.append(block_bug_1)
+on_event(lambda a: a.type == AppEvent.BLOCK_MOVED and a.block == block_player, lambda a: ai_bug(block_bug_1))
+
+block_bug_2 = [(1, 2, 2), tile_bug, BlockType.BUG]
+blocks.append(block_bug_2)
+on_event(lambda a: a.type == AppEvent.BLOCK_MOVED and a.block == block_player, lambda a: ai_bug(block_bug_2))
 
 update_blocks()
 
 
 scale_cache = {} # { surface: ((x, y), result_surface) }
 def scale_surface_to(surface, dest_size):
-	#return pygame.transform.smoothscale(surface, dest_size)
-	#return pygame.transform.scale(surface, dest_size)
-	if surface in scale_cache:
-		#print('Found ' + str(surface))
-		if scale_cache[surface][0] == dest_size:
-			#print('Found ' + str(dest_size))
-			return scale_cache[surface][1]
-		else:
-			print('Did not find ' + str(dest_size))
-	scale_cache[surface] = (dest_size, pygame.transform.smoothscale(surface, dest_size))
+	if not (surface in scale_cache and scale_cache[surface][0] == dest_size):
+		scale_cache[surface] = (dest_size, pygame.transform.smoothscale(surface, dest_size))
 	return scale_cache[surface][1]
 
 def draw_tile(tile, position):
 			)
 		)
 	)
+
+"""tile_to_fog_surface = {} # { tile: (surface, color)
+def draw_tile_fog(tile, position):
+	if tile in tile_to_fog_surface:
+		surface = tile_to_fog_surface[tile][0]
+	else:
+		surface = tile[0].copy()
+		tile_to_fog_surface[tile] = (surface, None)
+		#surface.fill((255, 0, 0), special_flags=pygame.BLEND_RGBA_MULT)
+	#surface = tile[0].copy()
+	#pygame.draw.rect(surface, pygame.Color(0, 0, 0), surface.get_rect())
+	
+	if tile_to_fog_surface[tile][1] == None:
+		surface.blit(tile[0], (0, 0))
+		surface.fill((255, (position[2] - camera[1]) * 20 + 127, 0), special_flags=pygame.BLEND_RGBA_MULT)
+	draw_tile((surface, tile[1], tile[2], tile[3]), position)
+###"""
+
 def draw_environment(event):
 	def is_filler_at(position):
-		if not is_block_at(position): return False
-		return position_to_block[position][1][3]
-	
-	#if blocks_changed:
-	#	update_blocks()
+		for block in get_blocks_at(position):
+			if block[1][3]:
+				return True
+		return False
 	
 	for block in blocks:
 		tile = block[1]
 		position = block[0]
 		draw_tile(tile, position)
+		#draw_tile_fog(tile, position)
 		if tile[3]:
 			### surface shadows
 			if not is_filler_at(sum3d(position, (0, 1, 0))):
 			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)
+			
+		#draw_tile_fog(tile, position)
 		
 	#pygame.draw.rect(surface_screen, colorHud, pygame.Rect(300 - 2, 200 - 2, 4, 4)) # centered guide dot
 on_event(lambda a: a.type == EVENT_DRAW, draw_environment)
 
 
 
-on_event(lambda a: a.type == EVENT_IN_NORTH, lambda a: move_player(block_player, (0, 0, -1)))
-on_event(lambda a: a.type == EVENT_IN_SOUTH, lambda a: move_player(block_player, (0, 0, 1)))
-on_event(lambda a: a.type == EVENT_IN_EAST, lambda a: move_player(block_player, (1, 0, 0)))
-on_event(lambda a: a.type == EVENT_IN_WEST, lambda a: move_player(block_player, (-1, 0, 0)))
+on_event(lambda a: a.type == AppEvent.INPUT_DIRECTION and a.direction == Direction.NORTH, lambda a: move_player(block_player, (0, 0, -1)))
+on_event(lambda a: a.type == AppEvent.INPUT_DIRECTION and a.direction == Direction.SOUTH, lambda a: move_player(block_player, (0, 0, 1)))
+on_event(lambda a: a.type == AppEvent.INPUT_DIRECTION and a.direction == Direction.EAST, lambda a: move_player(block_player, (1, 0, 0)))
+on_event(lambda a: a.type == AppEvent.INPUT_DIRECTION and a.direction == Direction.WEST, lambda a: move_player(block_player, (-1, 0, 0)))
 
 on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_EQUALS, lambda a: zoom_camera(-1))
 on_event(lambda a: a.type == pygame.KEYDOWN and a.key == pygame.K_MINUS, lambda a: zoom_camera(1))