Bugs fixed, ZorkUI hacked to work.
[realism.git] / engine / game_map.py
blobf27318122aa14639a06515467aad2b9d8e0767ba
1 (north, south, east, west) = range(4)
2 border_names = ("north", "south", "east", "west")
3 special_keys = ("encounter_frequency", "encounter_delay", "encounter_min_level", "encounter_max_level", "encounter_min_critters", "encounter_max_critters", "critter_types", "tileset")
5 from shop import make_shop
6 from random import randint
7 from battle import simple_battle
8 import tiles
10 class MapException(Exception):
11 pass
13 class game_map(object):
14 def __init__(self, name, map_lines):
15 self.name = name
16 self.game_map = []
17 self.tiles = []
18 self.map_key = {}
19 self.delay_left = 0
20 (ascii_art, key) = range(2)
21 phase = ascii_art
23 line_num = 0
24 for line in map_lines:
25 line_num += 1
26 line = line.rstrip()
27 if len(line) == 0 or line[0] == "'":
28 #Comment or blank line. Ignore it.
29 pass
30 elif ":" not in line:
31 if phase != ascii_art:
32 raise MapException("Error in processing map %s: non-key line %d found after start of key." % (name, line_num))
33 self.game_map.append(line)
34 else:
35 phase = key
36 (key, value_unsplit) = line.split(':')
37 value = value_unsplit.split()
38 self.map_key[key] = value
40 def is_tile(self, key):
41 return hasattr(tiles, key)
43 def get_tile_class(self, key):
44 return getattr(tiles, key)
46 def is_game_map(self, key):
47 return key in self.maps.keys()
49 def get_game_map(self, key):
50 return self.maps[key]
52 def make_tile(self, char, pos):
53 tile_key = self.map_key[char][0]
54 tile_args = self.map_key[char][1:]
55 if self.is_tile(tile_key):
56 tile_class = self.get_tile_class(tile_key)
57 return tile_class((self, pos), *tile_args)
58 elif self.is_game_map(tile_key):
59 game_map = self.get_game_map(tile_key)
60 return tiles.map_tile((self, pos), game_map, *tile_args)
61 else:
62 raise MapException("No tile or map named %s. (Used by symbol %s in map %s)" % (tile_key, char, self.name))
64 def validate(self, maps):
65 # We'll need this later.
66 self.maps = maps
68 # Look for bad characters, collect all starting positions, and initialize
69 # tiles.
70 starts = []
71 max_row_len = 0
72 for row_num in range(len(self.game_map)):
73 row = self.game_map[row_num]
74 row_len = len(row)
75 max_row_length = max(row_len, max_row_len)
76 self.tiles.append([None]*row_len)
77 for char_num in range(row_len):
78 char = row[char_num]
79 if char not in self.map_key.keys():
80 message = "Error in processing map %s: Character missing from key. Line %d:\n" % (self.name, row_num + 1)
81 message += row + "\n"
82 message += (" " * char_num) + "^"
83 raise MapException(message)
84 if self.map_key[char][0] in ("start", "start_and_movement_tutorial"):
85 starts.append([row_num, char_num])
86 self.tiles[row_num][char_num] = self.make_tile(char, (row_num, char_num))
88 self.borders = [None] * 4
89 for border in range(4):
90 key = "border_" + border_names[border]
91 self.borders[border] = self.make_tile(key, (-1, border))
93 # More than one start point is fatal, 0 is bad but non-fatal.
94 # If there is one, that's the starting position until set differently.
95 if len(starts) > 1:
96 raise MapException("More than one start point in map %s." % self.name)
97 elif len(starts) == 0:
98 print "Warning, map %s has no start point." % self.name
99 self.start = None
100 else:
101 self.start = starts[0]
102 self.pos = self.start
104 # Each value in the map key should be either a function or map. If neither,
105 # that's a fatal error.
106 for key in self.map_key.keys():
107 if key in special_keys:
108 continue
109 value = self.map_key[key][0]
110 if not hasattr(tiles, value):
111 if value not in maps.keys():
112 raise MapException("No tile or map named %s. (Used by symbol %s in map %s)" % (value, key, self.name))
114 def go(self, direction):
115 pos = self.pos
116 if pos != None:
117 newpos = [p for p in pos]
118 if direction == north:
119 newpos[0] -= 1
120 elif direction == south:
121 newpos[0] += 1
122 elif direction == west:
123 newpos[1] -= 1
124 elif direction == east:
125 newpos[1] += 1
126 return self.go_tile_at(newpos)
128 def go_tile_at(self, pos):
129 tile = self.get_tile(pos)
131 # Check to see if we moved.
132 moved = tile.passable()
134 # If we moved, update self.pos.
135 if (moved == True) and (self.border(pos) == None):
136 self.pos = pos
137 # Passable returns "None" when the player is transported off the map.
138 # So we reset the position on this map.
139 elif moved == None:
140 self.pos = self.start
142 # Do the tile's action regardless.
143 tile.action()
144 return moved
146 def border(self, pos):
147 (row, char) = pos
148 # Return the appropriate border if it's beyond the edge of the map.
149 if row < 0:
150 return north
151 elif char < 0:
152 return west
153 elif row >= len(self.tiles):
154 return south
155 elif char >= len(self.tiles[row]):
156 return east
157 else:
158 return None
160 def get_tile(self, pos):
161 (row, char) = pos
163 border = self.border(pos)
164 if border == None:
165 return self.tiles[row][char]
166 else:
167 return self.borders[border]
169 def is_shop(self):
170 return hasattr(self.get_tile(self.pos), "shop")
172 def enter_shop(self):
173 if not self.is_shop():
174 return False
176 # Get the shop.
177 shop = self.get_tile(self.pos).shop
179 # Show the shop.
180 self.game.ui.shop_mode(shop)
182 def hp_mp_ticks(self):
183 party = self.game.party
184 for i in range(len(party.chars)):
185 self.game.ui.hp_tick(i, party.chars[i].hp_tick())
186 self.game.ui.mp_tick(i, party.chars[i].mp_tick())
188 def get_window(self):
189 '''Returns a grid of tiles, centered on the player's location.'''
190 (row, col) = self.pos
191 if "window_size" in self.map_key.keys():
192 window_size = int(self.map_key["window_size"])
193 else:
194 window_size = 2
195 rowrange = range(row - window_size, row + window_size + 1)
196 colrange = range(col - window_size, col + window_size + 1)
198 window = tuple(tuple(self.get_tile((r, c)) for c in colrange) for r in rowrange)
199 return window
201 def make_make_map(maps_file, maps_dict):
202 from os.path import dirname, abspath, join
203 directory = dirname(abspath(maps_file))
204 def make_map(map_name):
205 map_file = open(join(directory, map_name + ".map"))
206 the_map = game_map(map_name, map_file.readlines())
207 maps_dict[map_name] = the_map
208 return the_map
209 return make_map