comparison tools/area_editor.py @ 241:ec567098cf41

Merge
author David Sharpe
date Wed, 04 Sep 2013 01:23:43 +0200
parents 42c565c5ce76
children 06c52529e2ed
comparison
equal deleted inserted replaced
240:f89576cec59a 241:ec567098cf41
21 21
22 import pymunk 22 import pymunk
23 23
24 from albow.root import RootWidget 24 from albow.root import RootWidget
25 from albow.widget import Widget 25 from albow.widget import Widget
26 from albow.controls import Button 26 from albow.controls import Button, Label, CheckBox
27 from albow.dialogs import alert 27 from albow.dialogs import alert
28 28
29 from nagslang.options import parse_args
29 from nagslang.constants import SCREEN 30 from nagslang.constants import SCREEN
30 from nagslang.level import Level, POLY_COLORS 31 from nagslang.level import Level, POLY_COLORS, LINE_COLOR
32 from nagslang.enemies import Enemy
31 33
32 34
33 # layout constants 35 # layout constants
34 MENU_BUTTON_HEIGHT = 35 36 MENU_BUTTON_HEIGHT = 35
35 MENU_PAD = 6 37 MENU_PAD = 6
58 self.polygons.setdefault(poly_index, []) 60 self.polygons.setdefault(poly_index, [])
59 if not self.polygons[poly_index]: 61 if not self.polygons[poly_index]:
60 point = self.point_to_pymunk(self.round_point(pos)) 62 point = self.point_to_pymunk(self.round_point(pos))
61 self.polygons[poly_index].append(point) 63 self.polygons[poly_index].append(point)
62 else: 64 else:
63 add_pos = self.fix_angle(poly_index, pos) 65 add_pos = self.fix_poly_angle(poly_index, pos)
64 self.polygons[poly_index].append(add_pos) 66 self.polygons[poly_index].append(add_pos)
65 67
66 def fix_angle(self, index, pos): 68 def _fix_angle(self, point1, pos):
67 # Last point
68 point1 = self.point_to_pygame(self.polygons[index][-1])
69 pos = self.round_point(pos)
70 # We want the line (point1 to pos) to be an angle of 69 # We want the line (point1 to pos) to be an angle of
71 # 0, 45, 90, 135, 180, 225, 270, 305 70 # 0, 45, 90, 135, 180, 225, 270, 305
72 # However, we only need to consider half the circle 71 # However, we only need to consider half the circle
73 # This is a hack to approximate the right thing 72 # This is a hack to approximate the right thing
74 pos0 = (pos[0], point1[1]) 73 pos0 = (pos[0], point1[1])
85 if dist < min_dist: 84 if dist < min_dist:
86 new_pos = cand 85 new_pos = cand
87 min_dist = dist 86 min_dist = dist
88 return self.point_to_pymunk(new_pos) 87 return self.point_to_pymunk(new_pos)
89 88
89 def fix_line_angle(self, start_pos, pos):
90 start_pos = self.round_point(start_pos)
91 pos = self.round_point(pos)
92 return self._fix_angle(start_pos, pos)
93
94 def fix_poly_angle(self, index, pos):
95 # Last point
96 point1 = self.point_to_pygame(self.polygons[index][-1])
97 pos = self.round_point(pos)
98 return self._fix_angle(point1, pos)
99
90 def delete_point(self, index): 100 def delete_point(self, index):
91 if index in self.polygons and len(self.polygons[index]) > 0: 101 if index in self.polygons and len(self.polygons[index]) > 0:
92 self.polygons[index].pop() 102 self.polygons[index].pop()
93 103
94 def close_poly(self, index): 104 def close_poly(self, index):
98 it's limited as it's a magic point addition""" 108 it's limited as it's a magic point addition"""
99 if len(self.polygons[index]) < 2: 109 if len(self.polygons[index]) < 2:
100 # Too small 110 # Too small
101 return False 111 return False
102 first = self.polygons[index][0] 112 first = self.polygons[index][0]
103 if self.fix_angle(index, self.point_to_pygame(first)) == first: 113 if self.fix_poly_angle(index, self.point_to_pygame(first)) == first:
104 self.add_point(index, self.point_to_pygame(first)) 114 self.add_point(index, self.point_to_pygame(first))
105 return True 115 return True
106 candidates = [(first[0] + 10 * i, first[1]) for 116 candidates = [(first[0] + 10 * i, first[1]) for
107 i in (-3, -2, -1, 1, 2, 3)] 117 i in (-3, -2, -1, 1, 2, 3)]
108 candidates.extend([(first[0], first[1] + 10 * i) for 118 candidates.extend([(first[0], first[1] + 10 * i) for
112 candidates.extend([(first[0] + 10 * i, first[1] - 10 * i) for 122 candidates.extend([(first[0] + 10 * i, first[1] - 10 * i) for
113 i in (-3, -2, -1, 1, 2, 3)]) 123 i in (-3, -2, -1, 1, 2, 3)])
114 min_dist = 99999 124 min_dist = 99999
115 poss = None 125 poss = None
116 for cand in candidates: 126 for cand in candidates:
117 if self.fix_angle(index, self.point_to_pygame(cand)) == cand: 127 if self.fix_poly_angle(index, self.point_to_pygame(cand)) == cand:
118 dist = (first[0] - cand[0]) ** 2 + (first[1] - cand[1]) ** 2 128 dist = (first[0] - cand[0]) ** 2 + (first[1] - cand[1]) ** 2
119 if dist < min_dist: 129 if dist < min_dist:
120 poss = cand 130 poss = cand
121 if poss is not None: 131 if poss is not None:
122 self.add_point(index, self.point_to_pygame(poss)) 132 self.add_point(index, self.point_to_pygame(poss))
123 self.add_point(index, self.point_to_pygame(first)) 133 self.add_point(index, self.point_to_pygame(first))
124 return True 134 return True
125 return False 135 return False
126 136
127 def draw(self, surface, topleft, mouse_pos, mouse_poly, filled): 137 def add_line(self, start_pos, end_pos):
138 endpoint = self.fix_line_angle(start_pos, end_pos)
139 startpoint = self.point_to_pymunk(self.round_point(start_pos))
140 self.lines.append([startpoint, endpoint])
141
142 def draw(self, mouse_pos, mouse_poly, filled, draw_cand_line, start_pos):
128 self._draw_background(True) 143 self._draw_background(True)
129 # Draw polygons as needed for the editor 144 # Draw polygons as needed for the editor
130 if filled: 145 if filled:
131 self._draw_exterior(True) 146 self._draw_exterior(True)
132 for index, polygon in self.polygons.items(): 147 for index, polygon in self.polygons.items():
133 color = POLY_COLORS[index] 148 color = POLY_COLORS[index]
134 if len(polygon) > 1: 149 if len(polygon) > 1:
135 pointlist = [self.point_to_pygame(p) for p in polygon] 150 pointlist = [self.point_to_pygame(p) for p in polygon]
136 pygame.draw.lines(self._surface, color, False, pointlist, 2) 151 pygame.draw.lines(self._surface, color, False, pointlist, 2)
137 if index == mouse_poly and mouse_pos: 152 if index == mouse_poly and mouse_pos:
138 endpoint = self.fix_angle(index, mouse_pos) 153 endpoint = self.fix_poly_angle(index, mouse_pos)
139 pygame.draw.line(self._surface, color, 154 pygame.draw.line(self._surface, color,
140 self.point_to_pygame(polygon[-1]), 155 self.point_to_pygame(polygon[-1]),
141 self.point_to_pygame(endpoint)) 156 self.point_to_pygame(endpoint))
142 surface_area = pygame.rect.Rect(topleft, SCREEN) 157 for line in self.lines:
143 surface.blit(self._surface, (0, 0), surface_area) 158 pointlist = [self.point_to_pygame(p) for p in line]
159 pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 2)
160 if draw_cand_line and start_pos and mouse_pos:
161 endpoint = self.fix_line_angle(start_pos, mouse_pos)
162 pointlist = [self.round_point(start_pos),
163 self.point_to_pygame(endpoint)]
164 pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 1)
165 return self._surface.copy()
144 166
145 167
146 class LevelWidget(Widget): 168 class LevelWidget(Widget):
147 169
148 def __init__(self, level): 170 def __init__(self, level):
152 self.pos = (0, 0) 174 self.pos = (0, 0)
153 self.filled_mode = False 175 self.filled_mode = False
154 self.mouse_pos = None 176 self.mouse_pos = None
155 self.cur_poly = None 177 self.cur_poly = None
156 self._mouse_drag = False 178 self._mouse_drag = False
179 self._draw_objects = False
180 self._draw_enemies = False
181 self._draw_lines = False
182 self._start_pos = None
157 183
158 def _level_coordinates(self, pos): 184 def _level_coordinates(self, pos):
159 # Move positions to level values 185 # Move positions to level values
160 if not pos: 186 if not pos:
161 return (0, 0) 187 return (0, 0)
171 new_pos[1] = self.pos[1] 197 new_pos[1] = self.pos[1]
172 elif new_pos[1] > self.level.y - SCREEN[1]: 198 elif new_pos[1] > self.level.y - SCREEN[1]:
173 new_pos[1] = self.pos[1] 199 new_pos[1] = self.pos[1]
174 self.pos = tuple(new_pos) 200 self.pos = tuple(new_pos)
175 201
202 def set_objects(self, value):
203 if self._draw_objects != value:
204 self._draw_objects = value
205 self.invalidate()
206
207 def set_enemies(self, value):
208 if self._draw_enemies != value:
209 self._draw_enemies = value
210 self.invalidate()
211
176 def draw(self, surface): 212 def draw(self, surface):
177 if (self.cur_poly is not None and self.cur_poly in self.level.polygons 213 if (self.cur_poly is not None and self.cur_poly in self.level.polygons
178 and len(self.level.polygons[self.cur_poly])): 214 and len(self.level.polygons[self.cur_poly])):
179 # We have an active polygon 215 # We have an active polygon
180 mouse_pos = self._level_coordinates(self.mouse_pos) 216 mouse_pos = self._level_coordinates(self.mouse_pos)
217 elif self._draw_lines:
218 # Interior wall mode
219 mouse_pos = self._level_coordinates(self.mouse_pos)
181 else: 220 else:
182 mouse_pos = None 221 mouse_pos = None
183 level.draw(surface, self.pos, mouse_pos, self.cur_poly, 222 level_surface = level.draw(mouse_pos, self.cur_poly, self.filled_mode,
184 self.filled_mode) 223 self._draw_lines, self._start_pos)
224 if self._draw_objects:
225 for thing in self.level.drawables:
226 if not isinstance(thing, Enemy):
227 thing.render(level_surface)
228 if self._draw_enemies:
229 for thing in self.level.drawables:
230 if isinstance(thing, Enemy):
231 thing.render(level_surface)
232 surface_area = pygame.rect.Rect(self.pos, SCREEN)
233 surface.blit(level_surface, (0, 0), surface_area)
185 234
186 def change_poly(self, new_poly): 235 def change_poly(self, new_poly):
187 self.cur_poly = new_poly 236 self.cur_poly = new_poly
237 self._draw_lines = False
188 if self.cur_poly is not None: 238 if self.cur_poly is not None:
189 self.filled_mode = False 239 self.filled_mode = False
240
241 def line_mode(self):
242 self.cur_poly = None
243 self._draw_lines = True
244 self.filled_mode = False
245 self._start_pos = None
190 246
191 def key_down(self, ev): 247 def key_down(self, ev):
192 if ev.key == pgl.K_LEFT: 248 if ev.key == pgl.K_LEFT:
193 self._move_view((-10, 0)) 249 self._move_view((-10, 0))
194 elif ev.key == pgl.K_RIGHT: 250 elif ev.key == pgl.K_RIGHT:
211 def set_filled(self): 267 def set_filled(self):
212 closed, _ = self.level.all_closed() 268 closed, _ = self.level.all_closed()
213 if closed: 269 if closed:
214 self.cur_poly = None 270 self.cur_poly = None
215 self.filled_mode = True 271 self.filled_mode = True
272 self._draw_lines = False
216 else: 273 else:
217 alert('Not all polygons closed, so not filling') 274 alert('Not all polygons closed, so not filling')
218 275
219 def mouse_move(self, ev): 276 def mouse_move(self, ev):
220 old_pos = self.mouse_pos 277 old_pos = self.mouse_pos
221 self.mouse_pos = ev.pos 278 self.mouse_pos = ev.pos
222 if self.cur_poly and old_pos != self.mouse_pos: 279 if old_pos != self.mouse_pos and (self.cur_poly or self._draw_lines):
223 self.invalidate() 280 self.invalidate()
224 281
225 def mouse_drag(self, ev): 282 def mouse_drag(self, ev):
226 if self._mouse_drag: 283 if self._mouse_drag:
227 old_pos = self.mouse_pos 284 old_pos = self.mouse_pos
231 self._move_view(diff) 288 self._move_view(diff)
232 self.invalidate() 289 self.invalidate()
233 290
234 def mouse_down(self, ev): 291 def mouse_down(self, ev):
235 if ev.button == 1: 292 if ev.button == 1:
236 print "Click: %r" % ( 293 if self._draw_lines:
237 self.level.point_to_pymunk(self._level_coordinates(ev.pos)),) 294 if self._start_pos is None:
295 self._start_pos = ev.pos
296 else:
297 self.level.add_line(self._start_pos, ev.pos)
298 self._start_pos = None
299 else:
300 print "Click: %r" % (
301 self.level.point_to_pymunk(
302 self._level_coordinates(ev.pos)),)
238 if ev.button == 4: # Scroll up 303 if ev.button == 4: # Scroll up
239 self._move_view((0, -10)) 304 self._move_view((0, -10))
240 elif ev.button == 5: # Scroll down 305 elif ev.button == 5: # Scroll down
241 self._move_view((0, 10)) 306 self._move_view((0, 10))
242 elif ev.button == 6: # Scroll left 307 elif ev.button == 6: # Scroll left
302 y += MENU_BUTTON_HEIGHT + MENU_PAD 367 y += MENU_BUTTON_HEIGHT + MENU_PAD
303 self.add(but) 368 self.add(but)
304 369
305 button_rect = pygame.rect.Rect(0, 0, MENU_WIDTH, MENU_BUTTON_HEIGHT) 370 button_rect = pygame.rect.Rect(0, 0, MENU_WIDTH, MENU_BUTTON_HEIGHT)
306 371
372 check_rect = pygame.rect.Rect(0, 0, MENU_BUTTON_HEIGHT // 2,
373 MENU_BUTTON_HEIGHT // 2)
374
307 end_poly_but = PolyButton(None, self.level_widget) 375 end_poly_but = PolyButton(None, self.level_widget)
308 end_poly_but.rect = button_rect.copy() 376 end_poly_but.rect = button_rect.copy()
309 end_poly_but.rect.move_ip(MENU_LEFT, y) 377 end_poly_but.rect.move_ip(MENU_LEFT, y)
310 self.add(end_poly_but) 378 self.add(end_poly_but)
379 y += MENU_BUTTON_HEIGHT + MENU_PAD
380
381 draw_line = Button("Draw interior wall", self.level_widget.line_mode)
382 draw_line.rect = button_rect.copy()
383 draw_line.rect.move_ip(MENU_LEFT, y)
384 self.add(draw_line)
311 y += MENU_BUTTON_HEIGHT + MENU_PAD 385 y += MENU_BUTTON_HEIGHT + MENU_PAD
312 386
313 fill_but = Button('Fill exterior', action=self.level_widget.set_filled) 387 fill_but = Button('Fill exterior', action=self.level_widget.set_filled)
314 fill_but.rect = button_rect.copy() 388 fill_but.rect = button_rect.copy()
315 fill_but.rect.move_ip(MENU_LEFT, y) 389 fill_but.rect.move_ip(MENU_LEFT, y)
327 close_poly_but.rect = button_rect.copy() 401 close_poly_but.rect = button_rect.copy()
328 close_poly_but.rect.move_ip(MENU_LEFT, y) 402 close_poly_but.rect.move_ip(MENU_LEFT, y)
329 self.add(close_poly_but) 403 self.add(close_poly_but)
330 y += MENU_BUTTON_HEIGHT + MENU_PAD 404 y += MENU_BUTTON_HEIGHT + MENU_PAD
331 405
406 white = pygame.color.Color("white")
407 self.show_objs = CheckBox(fg_color=white)
408 self.show_objs.rect = check_rect.copy()
409 self.show_objs.rect.move_ip(MENU_LEFT, y)
410 label = Label("Show Objects", fg_color=white)
411 label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
412 self.add(self.show_objs)
413 self.add(label)
414 y += label.rect.height + MENU_PAD
415
416 self.show_enemies = CheckBox(fg_color=white)
417 self.show_enemies.rect = check_rect.copy()
418 self.show_enemies.rect.move_ip(MENU_LEFT, y)
419 label = Label("Show enemy start pos", fg_color=white)
420 label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
421 self.add(self.show_enemies)
422 self.add(label)
423 y += label.rect.height + MENU_PAD
424
332 quit_but = Button('Quit', action=self.quit) 425 quit_but = Button('Quit', action=self.quit)
333 quit_but.rect = button_rect.copy() 426 quit_but.rect = button_rect.copy()
334 quit_but.rect.move_ip(MENU_LEFT, y) 427 quit_but.rect.move_ip(MENU_LEFT, y)
335 self.add(quit_but) 428 self.add(quit_but)
336 429
353 alert("Failed to save level.\n\n%s" % '\n'.join(messages)) 446 alert("Failed to save level.\n\n%s" % '\n'.join(messages))
354 447
355 def mouse_move(self, ev): 448 def mouse_move(self, ev):
356 self.level_widget.mouse_move(ev) 449 self.level_widget.mouse_move(ev)
357 450
451 def draw(self, surface):
452 # Update checkbox state
453 self.level_widget.set_objects(self.show_objs.value)
454 self.level_widget.set_enemies(self.show_enemies.value)
455 super(EditorApp, self).draw(surface)
456
358 457
359 if __name__ == "__main__": 458 if __name__ == "__main__":
360 if len(sys.argv) not in [2, 4]: 459 if len(sys.argv) not in [2, 4]:
361 print 'Please supply a levelname or levelname and level size' 460 print 'Please supply a levelname or levelname and level size'
362 sys.exit() 461 sys.exit()
462 # Need to ensure we have defaults for rendering
463 parse_args([])
363 pygame.display.init() 464 pygame.display.init()
364 pygame.font.init() 465 pygame.font.init()
365 pygame.display.set_mode((SCREEN[0] + MENU_WIDTH, SCREEN[1]), 466 pygame.display.set_mode((SCREEN[0] + MENU_WIDTH, SCREEN[1]),
366 pgl.SWSURFACE) 467 pgl.SWSURFACE)
367 if len(sys.argv) == 2: 468 if len(sys.argv) == 2: