git.fiddlerwoaroof.com
main.py
2616bc7d
 # Copyright (c) 2013 Edward Langley
 # All rights reserved.
 # 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
 # are met:
 # 
 # Redistributions of source code must retain the above copyright notice,
 # this list of conditions and the following disclaimer.
 # 
 # Redistributions in binary form must reproduce the above copyright
 # notice, this list of conditions and the following disclaimer in the
 # documentation and/or other materials provided with the distribution.
 # 
 # Neither the name of the project's author nor the names of its
 # contributors may be used to endorse or promote products derived from
 # this software without specific prior written permission.
 # 
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
2c098630
 from __future__ import division
 import colorsys
 import ctypes
 import libtcodpy as libtcod
 import random
 import time
7b5ffdd9
 import yaml
2c098630
 
 inst = lambda a:a()
 @inst
 class Settings:
7b5ffdd9
 	def __init__(self):
de9b8c09
 		with file('data/settings.yaml') as f:
 			self.settings = yaml.safe_load(f)
 	def __getattr__(self, attr):
 		return self.settings[attr]
7b5ffdd9
 
 	SCREEN_WIDTH = 75
aa15ba39
 	SCREEN_HEIGHT = 20
7b5ffdd9
 	DISPLAY_HEIGHT = property(lambda self: self.SCREEN_HEIGHT+2)
2c098630
 	LIMIT_FPS = 20
 	BASE = 8
 	LEVELS = 3
 
 libtcod.console_init_root(Settings.SCREEN_WIDTH, Settings.DISPLAY_HEIGHT, 'Mapping', False)
aa15ba39
 libtcod.console_set_custom_font("terminal.png",libtcod.FONT_LAYOUT_ASCII_INROW|libtcod.FONT_TYPE_GREYSCALE)
2c098630
 #libtcod.console_set_fullscreen(True)
 con = libtcod.console_new(Settings.SCREEN_WIDTH, Settings.SCREEN_HEIGHT)
 blank = libtcod.console_new(Settings.SCREEN_WIDTH, Settings.SCREEN_HEIGHT)
 #for x in range(Settings.SCREEN_WIDTH):
 	#for y in range(Settings.SCREEN_HEIGHT):
 
 
 message_con = libtcod.console_new(Settings.SCREEN_WIDTH, 2)
aa15ba39
 libtcod.console_set_default_background(message_con,libtcod.red)
2c098630
 #libtcod.console_set_default_background(blank,libtcod.red)
 
 
 libtcod.sys_set_fps(Settings.LIMIT_FPS)
 
 import map
 level = 1
 mp = map.Map.rand_new(Settings.LEVELS, Settings.BASE) ## warning: exponential BASE ** LEVELS
 mapping = {}
 
 colors = {
 	':': colorsys.rgb_to_hsv(0.0,0.0,1.0),
aa15ba39
 	chr(5): colorsys.rgb_to_hsv(0.0,0.5,0.0),
2c098630
 	'*': colorsys.rgb_to_hsv(1.0,1.0,0.0),
 }
 
 df = 1/40.0
 for x in range(-20,20):
aa15ba39
 	char = random.choice(['.','.','.','*','*',';',':','"',"'",chr(5),'`'])
2c098630
 	if char in colors:
 		h,s,v = colors[char]
 		print s,v
 		color = colorsys.hsv_to_rgb(h,s/1.75,v/2)
 		bgcolor = colorsys.hsv_to_rgb(h,s/4,v/4)
 	else:
 		hue = x+21 * df
 		color = colorsys.hsv_to_rgb(1/hue,0.5,0.5)
 		bgcolor = colorsys.hsv_to_rgb(1/hue,0.25,0.25)
 
 	color = [int(h*255) for h in color]
 	bgcolor = [int(h*255) for h in bgcolor]
 	mapping[x] = (libtcod.Color(*color), char, libtcod.Color(*bgcolor))
 
 names = {
 	'.': ('open',True,False),
 	'*': ('meadow',True,False),
 	';': ('marsh',1-0.05,False), ## Note: 1-0.05 means a 5% chance of being opaque
 	':': ('pond',True,True),
 	'"': ('heath',True,False),
 	"'": ('marsh',1-0.15,False),
aa15ba39
 	chr(5): ('forest',False,True),
2c098630
 	'`': ('copse',1-0.5,False),
 }
 
 class LevelMap(object):
 	def __init__(self, map):
 		self.map = map
 		self.fovmaps = [None]
 
 	def get_tile_type(self, level,x,y):
9f8d7155
 		lvl = self.map.get_level(level)
 		tile_val = mapping.get(lvl[y][x],(None,' '))[1]
2c098630
 		return names.get(tile_val,('',True,True))
 
 	def get_fovmap(self,level):
 		if level >= len(self.fovmaps):
9f8d7155
 			levelmap = mp.get_level(level)
2c098630
 			self.fovmaps.append(libtcod.map_new(len(levelmap), len(levelmap[0])))
 			libtcod.map_clear(self.fovmaps[-1])
 		return self.fovmaps[level]
 
 	def has_level(self, level):
 		return 1 <= level < len(self.fovmaps)
 
 	def adj_map(self, level, x,y):
 		fovmap = self.get_fovmap(level)
 		__, trans,block = self.get_tile_type(level,x,y)
 		trans = random.random() < trans
 		libtcod.map_set_properties(fovmap, x,y, trans, not block)
 
 	def comp_fov(self, level, player_x, player_y, light_radius=10):
 		fovmap = self.get_fovmap(level)
 		libtcod.map_compute_fov(fovmap, player_x, player_y, light_radius, False)
 
 	def get_color(self, level,x,y, color):
 		fovmap = self.get_fovmap(level)
aa15ba39
 		if libtcod.map_is_in_fov(fovmap, x,y) and libtcod.map_is_walkable(fovmap,x,y):
2c098630
 			#print 'cell in fov', level,x,y, libtcod.map_get_width(fovmap), libtcod.map_get_height(fovmap)
 			color = [x/255 for x in color]
 			h,s,v = colorsys.rgb_to_hsv(*color)
 			r,g,b = [int(x*255) for x in colorsys.hsv_to_rgb(h,s, v+0.15)]
 			color = libtcod.Color(r,g,b)
 		return color
 
 	def get_walkable(self, level, x,y):
 		fovmap = self.get_fovmap(level)
 		return libtcod.map_is_walkable(fovmap,x,y)
 
 	def calculate_level(self, level):
 		if not self.has_level(level):
 			print 'calculate_level'
9f8d7155
 			lvl = mp.get_level(level)
 			for y,row in enumerate(lvl):
2c098630
 				for x,__ in enumerate(row):
 					lm.adj_map(level, x,y)
 
de9b8c09
 
 
2c098630
 lm = LevelMap(mp)
 lm.calculate_level(level)
 
 libtcod.console_set_default_foreground(message_con, libtcod.white)
 for c in [con,message_con]:
 	libtcod.console_set_default_foreground(c, libtcod.white)
aa15ba39
 	libtcod.console_clear(c)
2c098630
 
 offset_x, offset_y = 0,0
9f8d7155
 levelmap = mp.get_level(level)
 MAP_Y,MAP_X = len(levelmap), len(levelmap[0])
2c098630
 player_x, player_y = random.randrange(0,MAP_X),random.randrange(0,MAP_Y)
 if not lm.get_walkable(level,player_x,player_y):
 	player_x,player_y = [(x,y) for x in range(player_x-2,player_x+3) for y in range(player_y-2,player_y+3) if lm.get_walkable(level,x,y)][-1]
 import time
 ak=0
 while not libtcod.console_is_window_closed():
 	t0 = time.time()
 
aa15ba39
 	if player_x - offset_x >= Settings.SCREEN_WIDTH-5: offset_x += 5
 	elif player_x - offset_x < 5: offset_x -= 5
2c098630
 
aa15ba39
 	if player_y - offset_y >= Settings.SCREEN_HEIGHT-5: offset_y += 5
 	elif player_y - offset_y < 5: offset_y -= 5
2c098630
 
 	offset_y = min(offset_y,MAP_Y-Settings.SCREEN_HEIGHT)
 	offset_y = max(offset_y,0)
 
 	offset_x = min(offset_x,MAP_X-Settings.SCREEN_WIDTH)
 	offset_x = max(offset_x,0)
 
 	t1 = time.time()
 	lm.comp_fov(level, player_x, player_y,10)
aa15ba39
 	player_screen_pos = player_x-offset_x,player_y-offset_y
9f8d7155
 	for y,row in enumerate(levelmap[offset_y:offset_y+Settings.SCREEN_HEIGHT]):
2c098630
 		for x,cell in enumerate(row[offset_x:offset_x+Settings.SCREEN_WIDTH]):
 			color,char,bgcolor = mapping.get(cell, (libtcod.Color(0,0,0),' ',libtcod.Color(0,0,0)))
 
 			color = lm.get_color(level, x+offset_x, y+offset_y, color)
 			bgcolor = lm.get_color(level, x+offset_x, y+offset_y, bgcolor)
 
aa15ba39
 			if (x,y) == player_screen_pos:
7b5ffdd9
 				color = libtcod.Color(128,128,100)
aa15ba39
 				char = '\x01'
 
 
2c098630
 			libtcod.console_set_char_foreground(con, x,y, color)
 			libtcod.console_set_char_background(con, x,y, bgcolor)
 			libtcod.console_set_char(con,x,y,char)
 
 	print 'draw loop:', time.time()-t1
 
 	libtcod.console_print(message_con, 0,1,lm.get_tile_type(level, player_x,player_y)[0])
 	libtcod.console_blit(message_con, 0,0, Settings.SCREEN_WIDTH,2, 0, 0,Settings.DISPLAY_HEIGHT-2)
 
 	blit_x,blit_y = 0,0
 	blit_w,blit_h = Settings.SCREEN_WIDTH, Settings.SCREEN_HEIGHT
 	if blit_x + MAP_X < Settings.SCREEN_WIDTH:
 		blit_x = (Settings.SCREEN_WIDTH - (blit_x + MAP_X))/2
 		blit_x = int(blit_x)
 		blit_w = MAP_X
 	if blit_y + MAP_Y < Settings.SCREEN_HEIGHT:
 		blit_y = (Settings.SCREEN_HEIGHT - (blit_y + MAP_Y))/2
 		blit_y = int(blit_y)
 		blit_h = MAP_Y
 	libtcod.console_blit(con, 0,0, blit_w,blit_h , 0,blit_x,blit_y)
 	libtcod.console_flush()
 	libtcod.console_clear(message_con)
 
 	bk = (time.time()-t0)*1000
 	print 'time to keypress:', bk
 	print 'loop time:', ak+bk
 	key = libtcod.Key()
 	mouse = libtcod.Mouse()
 	libtcod.sys_wait_for_event(libtcod.EVENT_KEY_PRESS, key, mouse, False)
 	t0 = time.time()
 
 	olevel = level
 	diff = 1
 	if key.shift:
 		diff *= 10
 
 	alt = key.lalt | key.ralt
 	ox,oy = player_x,player_y
 	dest_x,dest_y = player_x,player_y
 	if key.vk == libtcod.KEY_DOWN and player_y < MAP_Y:
 		dest_y += diff
 		if alt:
 			dest_x -= diff
 	elif key.vk == libtcod.KEY_UP and dest_y > 0:
 		dest_y -= diff
 		if alt:
 			dest_x += diff
 	elif key.vk == libtcod.KEY_RIGHT and dest_x < MAP_X:
 		dest_x += diff
 		if alt:
 			dest_y += diff
 	elif key.vk == libtcod.KEY_LEFT and dest_x > 0:
 		dest_x -= diff
 		if alt:
 			dest_y -= diff
fe324e6d
 	elif key.vk == libtcod.KEY_ESCAPE and any([key.lctrl,key.rctrl]): break
2c098630
 	elif key.c == ord('>') and level < mp.depth: level += 1
 	elif key.c == ord('<') and level > 1: level -= 1
 
 	dest_x = min(dest_x,MAP_X-1)
 	dest_y = min(dest_y,MAP_Y-1)
 
 	dest_x = max(dest_x,0)
 	dest_y = max(dest_y,0)
 
 	if olevel == level:
 		libtcod.line_init(player_x,player_y, dest_x,dest_y)
 		nx,ny = libtcod.line_step()
 		while None not in {nx,ny}:
 			if not lm.get_walkable(level,nx,ny):
aa15ba39
 				libtcod.console_print(message_con, 0,0,'You cannot walk through a %s' % lm.get_tile_type(level, nx,ny)[0])
2c098630
 				break
 			print nx,ny
 			player_x,player_y = nx,ny
 			nx,ny = libtcod.line_step()
 
 	else:
 		player_prop = player_x/MAP_X, player_y/MAP_Y
 		offset_prop = offset_x/MAP_X, offset_y/MAP_Y
9f8d7155
 		levelmap = mp.get_level(level)
 		MAP_Y,MAP_X = len(levelmap), len(levelmap[0])
2c098630
 		libtcod.console_clear(0)
 		libtcod.console_clear(con)
 
 		print 'before:',offset_x, player_x, offset_x+Settings.SCREEN_WIDTH
 		player_x = int(MAP_X * player_prop[0])
 		player_y = int(MAP_Y * player_prop[1])
 
 		offset_x = int(player_x - Settings.SCREEN_WIDTH / 2)
 		if offset_x + Settings.SCREEN_WIDTH > MAP_X:
 			offset_x -= (offset_x+Settings.SCREEN_WIDTH) - MAP_X
 		offset_y = int(player_y - Settings.SCREEN_HEIGHT / 2)
 		if offset_y + Settings.SCREEN_HEIGHT > MAP_Y:
 			offset_y -= (offset_x+Settings.SCREEN_HEIGHT) - MAP_Y
 
 		lm.calculate_level(level)
 
 		if not lm.get_walkable(level,player_x,player_y):
 			player_x,player_y = [(x,y) for x in range(player_x-2,player_x+3) for y in range(player_y-2,player_y+3) if lm.get_walkable(level,x,y)][-1]
 
 
 
 	print 'time after keypress:', (time.time()-t0)*1000