Mercurial > nagslang
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: |