[71] | 1 | #!/usr/bin/env python
|
---|
| 2 |
|
---|
[51] | 3 | # The basic area editor
|
---|
| 4 | #
|
---|
| 5 | # To edit an existing level, use
|
---|
| 6 | # editor levelname
|
---|
| 7 | #
|
---|
| 8 | # To create a new level:
|
---|
| 9 | #
|
---|
[315] | 10 | # editor levelname <xsize> <ysize>
|
---|
[51] | 11 | # (size specified in pixels
|
---|
| 12 | #
|
---|
| 13 |
|
---|
[71] | 14 | import os
|
---|
| 15 | import sys
|
---|
| 16 |
|
---|
[51] | 17 | import pygame
|
---|
| 18 | import pygame.locals as pgl
|
---|
| 19 |
|
---|
[152] | 20 | sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
---|
| 21 |
|
---|
[153] | 22 | import pymunk
|
---|
| 23 |
|
---|
[108] | 24 | from albow.root import RootWidget
|
---|
| 25 | from albow.widget import Widget
|
---|
[198] | 26 | from albow.controls import Button, Label, CheckBox
|
---|
[225] | 27 | from albow.dialogs import alert, Dialog
|
---|
| 28 | from albow.layout import Row
|
---|
[251] | 29 | from albow.fields import TextField
|
---|
[225] | 30 | from albow.table_view import TableView, TableColumn
|
---|
[108] | 31 |
|
---|
[199] | 32 | from nagslang.options import parse_args
|
---|
[109] | 33 | from nagslang.constants import SCREEN
|
---|
[204] | 34 | from nagslang.level import Level, POLY_COLORS, LINE_COLOR
|
---|
[255] | 35 | from nagslang.yamlish import load_s
|
---|
[251] | 36 | import nagslang.enemies as ne
|
---|
| 37 | import nagslang.game_object as ngo
|
---|
| 38 | import nagslang.puzzle as np
|
---|
[51] | 39 |
|
---|
[108] | 40 | # layout constants
|
---|
| 41 | MENU_BUTTON_HEIGHT = 35
|
---|
[115] | 42 | MENU_PAD = 6
|
---|
| 43 | MENU_HALF_PAD = MENU_PAD // 2
|
---|
| 44 | MENU_LEFT = SCREEN[0] + MENU_HALF_PAD
|
---|
| 45 | MENU_WIDTH = 200 - MENU_PAD
|
---|
[108] | 46 |
|
---|
[220] | 47 | BUTTON_RECT = pygame.rect.Rect(0, 0, MENU_WIDTH, MENU_BUTTON_HEIGHT)
|
---|
| 48 | CHECK_RECT = pygame.rect.Rect(0, 0, MENU_BUTTON_HEIGHT // 2,
|
---|
| 49 | MENU_BUTTON_HEIGHT // 2)
|
---|
| 50 |
|
---|
[108] | 51 |
|
---|
[355] | 52 | class TestWorld(object):
|
---|
| 53 |
|
---|
| 54 | def __init__(self):
|
---|
| 55 | self.level_state = {}
|
---|
[407] | 56 | self.inventory = {}
|
---|
| 57 |
|
---|
| 58 |
|
---|
| 59 | def distance(tup1, tup2):
|
---|
| 60 | return (tup1[0] - tup2[0]) ** 2 + (tup1[1] - tup2[1]) ** 2
|
---|
[355] | 61 |
|
---|
| 62 |
|
---|
[51] | 63 | class EditorLevel(Level):
|
---|
| 64 |
|
---|
| 65 | def __init__(self, name, x=800, y=600):
|
---|
[355] | 66 | world = TestWorld()
|
---|
| 67 | super(EditorLevel, self).__init__(name, world)
|
---|
[51] | 68 | self.x = x
|
---|
| 69 | self.y = y
|
---|
[275] | 70 | # Lookup initiliasition info from the objects
|
---|
| 71 | self.lookup = {}
|
---|
[407] | 72 | self._move_poly = None
|
---|
[51] | 73 |
|
---|
| 74 | def round_point(self, pos):
|
---|
| 75 | return (10 * (pos[0] // 10), 10 * (pos[1] // 10))
|
---|
| 76 |
|
---|
[275] | 77 | def load(self, space):
|
---|
| 78 | super(EditorLevel, self).load(space)
|
---|
| 79 | # Needed to fill in the lookup dict
|
---|
| 80 | self.reset_objs()
|
---|
| 81 |
|
---|
[51] | 82 | def point_to_pymunk(self, pos):
|
---|
| 83 | # inverse of point_to_pygame
|
---|
| 84 | # (this is also the same as point_to_pygame, but a additional
|
---|
| 85 | # function for sanity later in pyweek).
|
---|
| 86 | return (pos[0], self.y - pos[1])
|
---|
| 87 |
|
---|
| 88 | def add_point(self, poly_index, pos):
|
---|
| 89 | self.polygons.setdefault(poly_index, [])
|
---|
| 90 | if not self.polygons[poly_index]:
|
---|
[99] | 91 | point = self.point_to_pymunk(self.round_point(pos))
|
---|
| 92 | self.polygons[poly_index].append(point)
|
---|
[51] | 93 | else:
|
---|
[205] | 94 | add_pos = self.fix_poly_angle(poly_index, pos)
|
---|
[51] | 95 | self.polygons[poly_index].append(add_pos)
|
---|
| 96 |
|
---|
[205] | 97 | def _fix_angle(self, point1, pos):
|
---|
[51] | 98 | # We want the line (point1 to pos) to be an angle of
|
---|
| 99 | # 0, 45, 90, 135, 180, 225, 270, 305
|
---|
| 100 | # However, we only need to consider half the circle
|
---|
| 101 | # This is a hack to approximate the right thing
|
---|
| 102 | pos0 = (pos[0], point1[1])
|
---|
| 103 | pos90 = (point1[0], pos[1])
|
---|
| 104 | dist = max(abs(point1[0] - pos[0]), abs(point1[1] - pos[1]))
|
---|
| 105 | pos45 = (point1[0] + dist, point1[1] + dist)
|
---|
| 106 | pos135 = (point1[0] + dist, point1[1] - dist)
|
---|
| 107 | pos225 = (point1[0] - dist, point1[1] - dist)
|
---|
| 108 | pos305 = (point1[0] - dist, point1[1] + dist)
|
---|
| 109 | min_dist = 9999999
|
---|
| 110 | new_pos = point1
|
---|
| 111 | for cand in [pos0, pos90, pos45, pos135, pos225, pos305]:
|
---|
[407] | 112 | dist = distance(pos, cand)
|
---|
[51] | 113 | if dist < min_dist:
|
---|
| 114 | new_pos = cand
|
---|
| 115 | min_dist = dist
|
---|
| 116 | return self.point_to_pymunk(new_pos)
|
---|
| 117 |
|
---|
[205] | 118 | def fix_line_angle(self, start_pos, pos):
|
---|
| 119 | start_pos = self.round_point(start_pos)
|
---|
| 120 | pos = self.round_point(pos)
|
---|
| 121 | return self._fix_angle(start_pos, pos)
|
---|
| 122 |
|
---|
| 123 | def fix_poly_angle(self, index, pos):
|
---|
| 124 | # Last point
|
---|
| 125 | point1 = self.point_to_pygame(self.polygons[index][-1])
|
---|
| 126 | pos = self.round_point(pos)
|
---|
| 127 | return self._fix_angle(point1, pos)
|
---|
| 128 |
|
---|
[51] | 129 | def delete_point(self, index):
|
---|
| 130 | if index in self.polygons and len(self.polygons[index]) > 0:
|
---|
| 131 | self.polygons[index].pop()
|
---|
| 132 |
|
---|
[135] | 133 | def close_poly(self, index):
|
---|
| 134 | """Attempts to close the current polygon.
|
---|
| 135 |
|
---|
| 136 | We allow a small additional step to close the polygon, but
|
---|
| 137 | it's limited as it's a magic point addition"""
|
---|
| 138 | if len(self.polygons[index]) < 2:
|
---|
| 139 | # Too small
|
---|
| 140 | return False
|
---|
| 141 | first = self.polygons[index][0]
|
---|
[205] | 142 | if self.fix_poly_angle(index, self.point_to_pygame(first)) == first:
|
---|
[135] | 143 | self.add_point(index, self.point_to_pygame(first))
|
---|
| 144 | return True
|
---|
| 145 | candidates = [(first[0] + 10 * i, first[1]) for
|
---|
| 146 | i in (-3, -2, -1, 1, 2, 3)]
|
---|
| 147 | candidates.extend([(first[0], first[1] + 10 * i) for
|
---|
| 148 | i in (-3, -2, -1, 1, 2, 3)])
|
---|
| 149 | candidates.extend([(first[0] + 10 * i, first[1] + 10 * i) for
|
---|
| 150 | i in (-3, -2, -1, 1, 2, 3)])
|
---|
| 151 | candidates.extend([(first[0] + 10 * i, first[1] - 10 * i) for
|
---|
| 152 | i in (-3, -2, -1, 1, 2, 3)])
|
---|
| 153 | min_dist = 99999
|
---|
| 154 | poss = None
|
---|
| 155 | for cand in candidates:
|
---|
[205] | 156 | if self.fix_poly_angle(index, self.point_to_pygame(cand)) == cand:
|
---|
[407] | 157 | dist = distance(first, cand)
|
---|
[135] | 158 | if dist < min_dist:
|
---|
| 159 | poss = cand
|
---|
| 160 | if poss is not None:
|
---|
| 161 | self.add_point(index, self.point_to_pygame(poss))
|
---|
| 162 | self.add_point(index, self.point_to_pygame(first))
|
---|
| 163 | return True
|
---|
| 164 | return False
|
---|
| 165 |
|
---|
[205] | 166 | def add_line(self, start_pos, end_pos):
|
---|
| 167 | endpoint = self.fix_line_angle(start_pos, end_pos)
|
---|
| 168 | startpoint = self.point_to_pymunk(self.round_point(start_pos))
|
---|
| 169 | self.lines.append([startpoint, endpoint])
|
---|
| 170 |
|
---|
[407] | 171 | def draw(self, mouse_pos, mouse_poly, filled, draw_cand_line, start_pos,
|
---|
| 172 | move_point_mode, move_point):
|
---|
[51] | 173 | self._draw_background(True)
|
---|
| 174 | # Draw polygons as needed for the editor
|
---|
[96] | 175 | if filled:
|
---|
| 176 | self._draw_exterior(True)
|
---|
[51] | 177 | for index, polygon in self.polygons.items():
|
---|
| 178 | color = POLY_COLORS[index]
|
---|
[407] | 179 | if move_point_mode and index == self._move_poly:
|
---|
| 180 | pointlist = [p for p in polygon]
|
---|
| 181 | pointlist = [self.point_to_pygame(p) if p != move_point else
|
---|
| 182 | mouse_pos for p in pointlist]
|
---|
| 183 | pygame.draw.lines(self._surface, color, False, pointlist, 2)
|
---|
| 184 | elif len(polygon) > 1:
|
---|
[51] | 185 | pointlist = [self.point_to_pygame(p) for p in polygon]
|
---|
| 186 | pygame.draw.lines(self._surface, color, False, pointlist, 2)
|
---|
[407] | 187 | elif index == mouse_poly and mouse_pos:
|
---|
[205] | 188 | endpoint = self.fix_poly_angle(index, mouse_pos)
|
---|
[51] | 189 | pygame.draw.line(self._surface, color,
|
---|
[99] | 190 | self.point_to_pygame(polygon[-1]),
|
---|
| 191 | self.point_to_pygame(endpoint))
|
---|
[407] | 192 | line_found = False # Hack for sane behaviour if lines overlap
|
---|
[204] | 193 | for line in self.lines:
|
---|
| 194 | pointlist = [self.point_to_pygame(p) for p in line]
|
---|
[407] | 195 | if move_point_mode and not self._move_poly and not line_found:
|
---|
| 196 | if move_point in line:
|
---|
| 197 | line_found = True
|
---|
| 198 | pointlist.remove(self.point_to_pygame(move_point))
|
---|
| 199 | pointlist.append(mouse_pos)
|
---|
[204] | 200 | pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 2)
|
---|
[205] | 201 | if draw_cand_line and start_pos and mouse_pos:
|
---|
| 202 | endpoint = self.fix_line_angle(start_pos, mouse_pos)
|
---|
| 203 | pointlist = [self.round_point(start_pos),
|
---|
[206] | 204 | self.point_to_pygame(endpoint)]
|
---|
[205] | 205 | pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 1)
|
---|
[199] | 206 | return self._surface.copy()
|
---|
[51] | 207 |
|
---|
[239] | 208 | def reset_objs(self):
|
---|
| 209 | # Reset the object state - needed when changing stuff
|
---|
| 210 | self.drawables = []
|
---|
| 211 | self.overlay_drawables = []
|
---|
[251] | 212 | self._glue = np.PuzzleGlue()
|
---|
[239] | 213 | for game_object_dict in self._game_objects:
|
---|
[275] | 214 | obj = self._create_game_object(pymunk.Space(), **game_object_dict)
|
---|
| 215 | self.lookup[obj] = game_object_dict
|
---|
[239] | 216 | for enemy_dict in self._enemies:
|
---|
[275] | 217 | obj = self._create_enemy(pymunk.Space(), **enemy_dict)
|
---|
| 218 | self.lookup[obj] = enemy_dict
|
---|
[239] | 219 |
|
---|
[251] | 220 | def get_class(self, classname, mod=None):
|
---|
| 221 | # Get the class given the classname
|
---|
| 222 | modules = {
|
---|
| 223 | 'game_object': ngo,
|
---|
| 224 | 'enemies': ne,
|
---|
| 225 | 'puzzle': np,
|
---|
| 226 | }
|
---|
| 227 | if '.' in classname:
|
---|
| 228 | modname, classname = classname.split('.')
|
---|
| 229 | mod = modules[modname]
|
---|
| 230 | if mod is None:
|
---|
| 231 | mod = ngo
|
---|
| 232 | return getattr(mod, classname)
|
---|
| 233 |
|
---|
[262] | 234 | def try_new_object(self, classname, target, new, old=None):
|
---|
[251] | 235 | if old in target:
|
---|
| 236 | target.remove(old)
|
---|
| 237 | try:
|
---|
| 238 | target.append(new)
|
---|
| 239 | self.reset_objs()
|
---|
| 240 | return True
|
---|
[262] | 241 | except Exception as e:
|
---|
[251] | 242 | target.remove(new)
|
---|
| 243 | if old is not None:
|
---|
| 244 | target.append(old)
|
---|
| 245 | self.reset_objs()
|
---|
[262] | 246 | alert("Failed to update object %s: %s" % (classname, e))
|
---|
[251] | 247 | return False
|
---|
| 248 |
|
---|
[275] | 249 | def find_obj_at_pos(self, mouse_pos):
|
---|
| 250 | pymunk_pos = self.point_to_pymunk(mouse_pos)
|
---|
| 251 | # Search visible objects
|
---|
| 252 | for obj in self.drawables:
|
---|
| 253 | if obj.get_shape().point_query(pymunk_pos):
|
---|
| 254 | return obj
|
---|
| 255 | return None
|
---|
| 256 |
|
---|
[407] | 257 | def find_vertex(self, mouse_pos):
|
---|
| 258 | # search for vertexes closest to where we've killed
|
---|
| 259 | mindist = 400
|
---|
| 260 | move_point = None
|
---|
| 261 | search_point = self.point_to_pymunk(mouse_pos)
|
---|
| 262 | for index, polygon in self.polygons.items():
|
---|
| 263 | for point in polygon:
|
---|
| 264 | dist = distance(point, search_point)
|
---|
| 265 | if dist < mindist:
|
---|
| 266 | mindist = dist
|
---|
| 267 | move_point = point
|
---|
| 268 | self._move_poly = index
|
---|
| 269 | # Also check lines
|
---|
| 270 | for line in self.lines:
|
---|
| 271 | for point in line:
|
---|
| 272 | dist = distance(point, search_point)
|
---|
| 273 | if dist < mindist:
|
---|
| 274 | mindist = dist
|
---|
| 275 | move_point = point
|
---|
| 276 | self._move_poly = None
|
---|
| 277 | return move_point
|
---|
| 278 |
|
---|
| 279 | def replace_vertex(self, old_point, new_point):
|
---|
| 280 | new_point = self.point_to_pymunk(new_point)
|
---|
| 281 | if self._move_poly:
|
---|
| 282 | new_polygon = [p if p != old_point else new_point for p in
|
---|
| 283 | self.polygons[self._move_poly]]
|
---|
| 284 | self.polygons[self._move_poly] = new_polygon
|
---|
| 285 | else:
|
---|
| 286 | for line in self.lines:
|
---|
| 287 | if old_point in line:
|
---|
| 288 | line.remove(old_point)
|
---|
| 289 | line.append(new_point)
|
---|
| 290 | break
|
---|
| 291 |
|
---|
[109] | 292 |
|
---|
[225] | 293 | class ObjectTable(TableView):
|
---|
| 294 |
|
---|
| 295 | columns = [TableColumn("Object", 690, 'l', '%r')]
|
---|
| 296 |
|
---|
| 297 | def __init__(self, data):
|
---|
| 298 | super(ObjectTable, self).__init__(height=450)
|
---|
| 299 | self.data = data
|
---|
| 300 | self.selected_row = -1
|
---|
| 301 |
|
---|
| 302 | def num_rows(self):
|
---|
| 303 | return len(self.data)
|
---|
| 304 |
|
---|
| 305 | def row_data(self, i):
|
---|
| 306 | data = self.data[i]
|
---|
| 307 | if 'name' in data:
|
---|
| 308 | return ('%s (%s)' % (data['classname'], data['name']), )
|
---|
| 309 | return (data['classname'], )
|
---|
| 310 |
|
---|
| 311 | def row_is_selected(self, i):
|
---|
| 312 | return self.selected_row == i
|
---|
| 313 |
|
---|
| 314 | def click_row(self, i, ev):
|
---|
| 315 | self.selected_row = i
|
---|
| 316 |
|
---|
| 317 | def get_selection(self):
|
---|
| 318 | if self.selected_row >= 0:
|
---|
| 319 | return self.data[self.selected_row]
|
---|
| 320 | return None
|
---|
| 321 |
|
---|
| 322 |
|
---|
[251] | 323 | class EditClassDialog(Dialog):
|
---|
| 324 |
|
---|
[355] | 325 | def __init__(self, classname, cls, data, level_widget,
|
---|
| 326 | delete=False):
|
---|
[251] | 327 | super(EditClassDialog, self).__init__()
|
---|
[355] | 328 | self.level_widget = level_widget
|
---|
[255] | 329 | self.classname = classname
|
---|
[355] | 330 | self.rect = pygame.rect.Rect(0, 0, 900, 550)
|
---|
[251] | 331 | title = Label("Editing %s" % classname)
|
---|
| 332 | title.rect = pygame.rect.Rect(100, 10, 600, 25)
|
---|
| 333 | self.add(title)
|
---|
[262] | 334 | self.requires = cls.requires()
|
---|
[251] | 335 | y = 40
|
---|
| 336 | self.fields = {}
|
---|
| 337 | index = 0
|
---|
[355] | 338 | self.poly_field = None
|
---|
| 339 | self.needs_cleanup = False
|
---|
[262] | 340 | for requirement, hint in self.requires:
|
---|
[251] | 341 | label = Label(requirement)
|
---|
| 342 | label.rect = pygame.rect.Rect(40, y, 200, 25)
|
---|
| 343 | self.add(label)
|
---|
| 344 | field = TextField()
|
---|
| 345 | field.rect = pygame.rect.Rect(220, y, 400, 25)
|
---|
| 346 | self.add(field)
|
---|
| 347 | if data is not None:
|
---|
| 348 | if requirement in data:
|
---|
| 349 | field.set_text('%s' % data[requirement])
|
---|
[255] | 350 | elif 'args' in data and requirement != 'name':
|
---|
[251] | 351 | # NB: The ordering assumptions in requires should make
|
---|
| 352 | # this safe, but it's really, really, really fragile
|
---|
[255] | 353 | try:
|
---|
| 354 | field.set_text('%s' % data['args'][index])
|
---|
| 355 | index += 1
|
---|
| 356 | except IndexError:
|
---|
| 357 | # Assumed to be arguments with the default value
|
---|
| 358 | pass
|
---|
[355] | 359 | if hint.startswith('polygon'):
|
---|
| 360 | self.poly_field = field
|
---|
[251] | 361 | self.fields[requirement] = field
|
---|
| 362 | hintlabel = Label(hint)
|
---|
[355] | 363 | hintlabel.rect = pygame.rect.Rect(640, y, 250, 25)
|
---|
[251] | 364 | self.add(hintlabel)
|
---|
| 365 | y += 30
|
---|
[355] | 366 | if self.poly_field:
|
---|
| 367 | y += 20
|
---|
| 368 | button = Button('Use Polygon 6', action=self.get_poly)
|
---|
| 369 | button.rect = pygame.rect.Rect(350, y, 250, 30)
|
---|
| 370 | self.add(button)
|
---|
[251] | 371 | buttons = []
|
---|
[275] | 372 | if delete:
|
---|
| 373 | labels = ['OK', 'Delete', 'Cancel']
|
---|
| 374 | else:
|
---|
| 375 | labels = ['OK', 'Cancel']
|
---|
| 376 | for text in labels:
|
---|
[251] | 377 | but = Button(text, action=lambda x=text: self.dismiss(x))
|
---|
| 378 | buttons.append(but)
|
---|
| 379 | row = Row(buttons)
|
---|
| 380 | row.rect = pygame.rect.Rect(250, 500, 700, 50)
|
---|
| 381 | self.add(row)
|
---|
| 382 |
|
---|
[355] | 383 | def get_poly(self):
|
---|
| 384 | try:
|
---|
| 385 | data = self.level_widget.level.polygons[6][:]
|
---|
| 386 | except KeyError:
|
---|
| 387 | data = []
|
---|
| 388 | if data:
|
---|
| 389 | # We unclose the polygon, because that's what pymunk
|
---|
| 390 | # wants
|
---|
| 391 | if data[0] == data[-1] and len(data) > 1:
|
---|
| 392 | data.pop()
|
---|
| 393 | data = [list(x) for x in data]
|
---|
| 394 | self.needs_cleanup = True
|
---|
| 395 | self.poly_field.set_text('%s' % data)
|
---|
| 396 |
|
---|
| 397 | def cleanup(self):
|
---|
| 398 | if self.needs_cleanup:
|
---|
| 399 | self.level_widget.level.polygons[6] = []
|
---|
| 400 | self.level_widget.invalidate()
|
---|
| 401 |
|
---|
[251] | 402 | def get_data(self):
|
---|
[255] | 403 | result = {}
|
---|
| 404 | result['classname'] = self.classname
|
---|
| 405 | args = []
|
---|
| 406 | # We arrange to bounce this through yaml'ish to convert
|
---|
| 407 | # stuff to the expected type
|
---|
[262] | 408 | for val, _ in self.requires:
|
---|
[255] | 409 | text = self.fields[val].get_text()
|
---|
| 410 | if not text:
|
---|
| 411 | # skip empty fields
|
---|
| 412 | continue
|
---|
| 413 | if val == 'name':
|
---|
| 414 | result['name'] = text
|
---|
[355] | 415 | elif self.fields[val] == self.poly_field and text:
|
---|
| 416 | # Evil, but faster than good
|
---|
| 417 | try:
|
---|
| 418 | l = eval(text)
|
---|
| 419 | args.append(' - - %s' % l[0])
|
---|
| 420 | for coord in l[1:]:
|
---|
| 421 | args.append(' - %s' % coord)
|
---|
| 422 | except Exception:
|
---|
| 423 | alert("Invalid polygon %s" % text)
|
---|
| 424 | self.needs_cleanup = False
|
---|
| 425 | return None
|
---|
| 426 | # Check for convexity
|
---|
| 427 | hull = pymunk.util.convex_hull(l)
|
---|
| 428 | if hull != l:
|
---|
| 429 | alert("Invalid polygon %s - not convex" % text)
|
---|
| 430 | return None
|
---|
[255] | 431 | else:
|
---|
| 432 | args.append(' - ' + text)
|
---|
| 433 | data = "args:\n" + '\n'.join(args)
|
---|
| 434 | result['args'] = load_s(data)['args']
|
---|
| 435 | return result
|
---|
[251] | 436 |
|
---|
| 437 |
|
---|
[108] | 438 | class LevelWidget(Widget):
|
---|
[51] | 439 |
|
---|
[280] | 440 | def __init__(self, level, parent):
|
---|
[108] | 441 | super(LevelWidget, self).__init__(pygame.rect.Rect(0, 0,
|
---|
[109] | 442 | SCREEN[0], SCREEN[1]))
|
---|
[51] | 443 | self.level = level
|
---|
| 444 | self.pos = (0, 0)
|
---|
[108] | 445 | self.filled_mode = False
|
---|
[51] | 446 | self.mouse_pos = None
|
---|
[108] | 447 | self.cur_poly = None
|
---|
[167] | 448 | self._mouse_drag = False
|
---|
[199] | 449 | self._draw_objects = False
|
---|
| 450 | self._draw_enemies = False
|
---|
[204] | 451 | self._draw_lines = False
|
---|
[275] | 452 | self.sel_mode = False
|
---|
[205] | 453 | self._start_pos = None
|
---|
[280] | 454 | self._parent = parent
|
---|
[407] | 455 | self._move_point_mode = False
|
---|
| 456 | self._move_point = False
|
---|
[51] | 457 |
|
---|
[108] | 458 | def _level_coordinates(self, pos):
|
---|
| 459 | # Move positions to level values
|
---|
| 460 | if not pos:
|
---|
| 461 | return (0, 0)
|
---|
| 462 | return pos[0] + self.pos[0], pos[1] + self.pos[1]
|
---|
| 463 |
|
---|
| 464 | def _move_view(self, offset):
|
---|
[51] | 465 | new_pos = [self.pos[0] + offset[0], self.pos[1] + offset[1]]
|
---|
| 466 | if new_pos[0] < 0:
|
---|
| 467 | new_pos[0] = self.pos[0]
|
---|
| 468 | elif new_pos[0] > self.level.x - SCREEN[0]:
|
---|
| 469 | new_pos[0] = self.pos[0]
|
---|
| 470 | if new_pos[1] < 0:
|
---|
| 471 | new_pos[1] = self.pos[1]
|
---|
| 472 | elif new_pos[1] > self.level.y - SCREEN[1]:
|
---|
| 473 | new_pos[1] = self.pos[1]
|
---|
| 474 | self.pos = tuple(new_pos)
|
---|
| 475 |
|
---|
[199] | 476 | def set_objects(self, value):
|
---|
| 477 | if self._draw_objects != value:
|
---|
| 478 | self._draw_objects = value
|
---|
| 479 | self.invalidate()
|
---|
| 480 |
|
---|
| 481 | def set_enemies(self, value):
|
---|
| 482 | if self._draw_enemies != value:
|
---|
| 483 | self._draw_enemies = value
|
---|
| 484 | self.invalidate()
|
---|
| 485 |
|
---|
[108] | 486 | def draw(self, surface):
|
---|
[51] | 487 | if (self.cur_poly is not None and self.cur_poly in self.level.polygons
|
---|
| 488 | and len(self.level.polygons[self.cur_poly])):
|
---|
| 489 | # We have an active polygon
|
---|
| 490 | mouse_pos = self._level_coordinates(self.mouse_pos)
|
---|
[205] | 491 | elif self._draw_lines:
|
---|
| 492 | # Interior wall mode
|
---|
| 493 | mouse_pos = self._level_coordinates(self.mouse_pos)
|
---|
[407] | 494 | elif self._move_point_mode:
|
---|
| 495 | mouse_pos = self._level_coordinates(self.mouse_pos)
|
---|
[51] | 496 | else:
|
---|
| 497 | mouse_pos = None
|
---|
[205] | 498 | level_surface = level.draw(mouse_pos, self.cur_poly, self.filled_mode,
|
---|
[407] | 499 | self._draw_lines, self._start_pos,
|
---|
| 500 | self._move_point_mode, self._move_point)
|
---|
[199] | 501 | if self._draw_objects:
|
---|
| 502 | for thing in self.level.drawables:
|
---|
[251] | 503 | if not isinstance(thing, ne.Enemy):
|
---|
[199] | 504 | thing.render(level_surface)
|
---|
| 505 | if self._draw_enemies:
|
---|
| 506 | for thing in self.level.drawables:
|
---|
[251] | 507 | if isinstance(thing, ne.Enemy):
|
---|
[199] | 508 | thing.render(level_surface)
|
---|
| 509 | surface_area = pygame.rect.Rect(self.pos, SCREEN)
|
---|
| 510 | surface.blit(level_surface, (0, 0), surface_area)
|
---|
[51] | 511 |
|
---|
[115] | 512 | def change_poly(self, new_poly):
|
---|
| 513 | self.cur_poly = new_poly
|
---|
[204] | 514 | self._draw_lines = False
|
---|
[115] | 515 | if self.cur_poly is not None:
|
---|
[280] | 516 | self._parent.reset_lit_buttons()
|
---|
[115] | 517 | self.filled_mode = False
|
---|
| 518 |
|
---|
[204] | 519 | def line_mode(self):
|
---|
| 520 | self.cur_poly = None
|
---|
[280] | 521 | self._parent.reset_lit_buttons()
|
---|
[204] | 522 | self._draw_lines = True
|
---|
| 523 | self.filled_mode = False
|
---|
[205] | 524 | self._start_pos = None
|
---|
[407] | 525 | self._move_point_mode = False
|
---|
[204] | 526 |
|
---|
[108] | 527 | def key_down(self, ev):
|
---|
| 528 | if ev.key == pgl.K_LEFT:
|
---|
| 529 | self._move_view((-10, 0))
|
---|
| 530 | elif ev.key == pgl.K_RIGHT:
|
---|
| 531 | self._move_view((10, 0))
|
---|
| 532 | elif ev.key == pgl.K_UP:
|
---|
| 533 | self._move_view((0, -10))
|
---|
| 534 | elif ev.key == pgl.K_DOWN:
|
---|
| 535 | self._move_view((0, 10))
|
---|
[115] | 536 | elif ev.key in (pgl.K_1, pgl.K_2, pgl.K_3, pgl.K_4, pgl.K_5, pgl.K_6):
|
---|
| 537 | self.change_poly(ev.key - pgl.K_0)
|
---|
[108] | 538 | elif ev.key == pgl.K_0:
|
---|
[115] | 539 | self.change_poly(None)
|
---|
[108] | 540 | elif ev.key == pgl.K_d and self.cur_poly:
|
---|
| 541 | self.level.delete_point(self.cur_poly)
|
---|
| 542 | elif ev.key == pgl.K_f:
|
---|
[117] | 543 | self.set_filled()
|
---|
[135] | 544 | elif ev.key == pgl.K_c:
|
---|
| 545 | self.close_poly()
|
---|
[117] | 546 |
|
---|
[407] | 547 | def set_move_mode(self):
|
---|
| 548 | self._draw_lines = False
|
---|
| 549 | self._move_point_mode = True
|
---|
| 550 | self.filled_mode = False
|
---|
| 551 | self._parent.reset_lit_buttons()
|
---|
| 552 | self._move_point = None
|
---|
| 553 |
|
---|
[117] | 554 | def set_filled(self):
|
---|
[165] | 555 | closed, _ = self.level.all_closed()
|
---|
| 556 | if closed:
|
---|
[117] | 557 | self.cur_poly = None
|
---|
[280] | 558 | self._parent.reset_lit_buttons()
|
---|
[117] | 559 | self.filled_mode = True
|
---|
[204] | 560 | self._draw_lines = False
|
---|
[407] | 561 | self._move_point_mode = False
|
---|
[117] | 562 | else:
|
---|
[165] | 563 | alert('Not all polygons closed, so not filling')
|
---|
[108] | 564 |
|
---|
[109] | 565 | def mouse_move(self, ev):
|
---|
| 566 | old_pos = self.mouse_pos
|
---|
| 567 | self.mouse_pos = ev.pos
|
---|
[407] | 568 | if old_pos != self.mouse_pos and (self.cur_poly or self._draw_lines
|
---|
| 569 | or self._move_point_mode):
|
---|
[109] | 570 | self.invalidate()
|
---|
| 571 |
|
---|
[167] | 572 | def mouse_drag(self, ev):
|
---|
| 573 | if self._mouse_drag:
|
---|
| 574 | old_pos = self.mouse_pos
|
---|
| 575 | self.mouse_pos = ev.pos
|
---|
| 576 | diff = (-self.mouse_pos[0] + old_pos[0],
|
---|
| 577 | -self.mouse_pos[1] + old_pos[1])
|
---|
| 578 | self._move_view(diff)
|
---|
| 579 | self.invalidate()
|
---|
| 580 |
|
---|
[109] | 581 | def mouse_down(self, ev):
|
---|
[407] | 582 | corrected_pos = ev.pos[0] + self.pos[0], ev.pos[1] + self.pos[1]
|
---|
[275] | 583 | if self.sel_mode and ev.button == 1:
|
---|
| 584 | obj = self.level.find_obj_at_pos(corrected_pos)
|
---|
| 585 | if obj is not None:
|
---|
| 586 | self._edit_selected(obj)
|
---|
[407] | 587 | elif self._move_point_mode and ev.button == 1:
|
---|
| 588 | if self._move_point:
|
---|
| 589 | # Place the current point
|
---|
| 590 | self.level.replace_vertex(self._move_point, corrected_pos)
|
---|
| 591 | self._move_point = None
|
---|
| 592 | self.invalidate()
|
---|
| 593 | else:
|
---|
| 594 | # find the current point
|
---|
| 595 | self._move_point = self.level.find_vertex(corrected_pos)
|
---|
[275] | 596 | elif ev.button == 1:
|
---|
[204] | 597 | if self._draw_lines:
|
---|
[205] | 598 | if self._start_pos is None:
|
---|
| 599 | self._start_pos = ev.pos
|
---|
| 600 | else:
|
---|
| 601 | self.level.add_line(self._start_pos, ev.pos)
|
---|
| 602 | self._start_pos = None
|
---|
[204] | 603 | else:
|
---|
| 604 | print "Click: %r" % (
|
---|
| 605 | self.level.point_to_pymunk(
|
---|
| 606 | self._level_coordinates(ev.pos)),)
|
---|
[157] | 607 | if ev.button == 4: # Scroll up
|
---|
| 608 | self._move_view((0, -10))
|
---|
| 609 | elif ev.button == 5: # Scroll down
|
---|
| 610 | self._move_view((0, 10))
|
---|
| 611 | elif ev.button == 6: # Scroll left
|
---|
| 612 | self._move_view((-10, 0))
|
---|
| 613 | elif ev.button == 7: # Scroll right
|
---|
| 614 | self._move_view((10, 0))
|
---|
[167] | 615 | elif self.cur_poly and ev.button == 1:
|
---|
[109] | 616 | # Add a point
|
---|
| 617 | self.level.add_point(self.cur_poly,
|
---|
| 618 | self._level_coordinates(ev.pos))
|
---|
[167] | 619 | elif ev.button == 3:
|
---|
| 620 | self._mouse_drag = True
|
---|
| 621 |
|
---|
| 622 | def mouse_up(self, ev):
|
---|
| 623 | if ev.button == 3:
|
---|
| 624 | self._mouse_drag = False
|
---|
[108] | 625 |
|
---|
[135] | 626 | def close_poly(self):
|
---|
| 627 | if self.cur_poly is None:
|
---|
| 628 | return
|
---|
| 629 | if self.level.close_poly(self.cur_poly):
|
---|
| 630 | alert("Successfully closed the polygon")
|
---|
| 631 | self.change_poly(None)
|
---|
| 632 | else:
|
---|
| 633 | alert("Failed to close the polygon")
|
---|
| 634 |
|
---|
[251] | 635 | def _edit_class(self, classname, cls, data):
|
---|
| 636 | # Dialog for class properties
|
---|
[355] | 637 | dialog = EditClassDialog(classname, cls, data, self)
|
---|
[251] | 638 | if dialog.present() == 'OK':
|
---|
[355] | 639 | return dialog
|
---|
[251] | 640 | return None
|
---|
| 641 |
|
---|
[275] | 642 | def _edit_selected(self, obj):
|
---|
| 643 | data = self.level.lookup[obj]
|
---|
| 644 | cls = obj.__class__
|
---|
| 645 | classname = obj.__class__.__name__
|
---|
[355] | 646 | dialog = EditClassDialog(classname, cls, data, self, True)
|
---|
[275] | 647 | res = dialog.present()
|
---|
| 648 | if res == 'OK':
|
---|
| 649 | edited = dialog.get_data()
|
---|
| 650 | if edited is not None:
|
---|
| 651 | for target in [self.level._game_objects, self.level._enemies]:
|
---|
| 652 | if data in target:
|
---|
[355] | 653 | if self.level.try_new_object(classname, target,
|
---|
| 654 | edited, data):
|
---|
| 655 | dialog.cleanup()
|
---|
[275] | 656 | break
|
---|
| 657 | elif res == 'Delete':
|
---|
| 658 | for target in [self.level._game_objects, self.level._enemies]:
|
---|
| 659 | if data in target:
|
---|
| 660 | target.remove(data)
|
---|
| 661 | self.level.reset_objs()
|
---|
| 662 | break
|
---|
| 663 |
|
---|
[225] | 664 | def _make_edit_dialog(self, entries):
|
---|
| 665 | # Dialog to hold the editor
|
---|
| 666 | edit_box = Dialog()
|
---|
| 667 | edit_box.rect = pygame.rect.Rect(0, 0, 700, 500)
|
---|
| 668 | table = ObjectTable(entries)
|
---|
| 669 | edit_box.add(table)
|
---|
| 670 | buttons = []
|
---|
| 671 | for text in ['OK', 'Delete', 'Cancel']:
|
---|
| 672 | but = Button(text, action=lambda x=text: edit_box.dismiss(x))
|
---|
| 673 | buttons.append(but)
|
---|
| 674 | row = Row(buttons)
|
---|
[251] | 675 | row.rect = pygame.rect.Rect(250, 450, 700, 50)
|
---|
[225] | 676 | edit_box.add(row)
|
---|
[236] | 677 | edit_box.get_selection = lambda: table.get_selection()
|
---|
[225] | 678 | return edit_box
|
---|
| 679 |
|
---|
| 680 | def edit_objects(self):
|
---|
| 681 | edit_box = self._make_edit_dialog(self.level._game_objects)
|
---|
| 682 | res = edit_box.present()
|
---|
[236] | 683 | choice = edit_box.get_selection()
|
---|
| 684 | if choice is None:
|
---|
| 685 | return
|
---|
[225] | 686 | if res == 'OK':
|
---|
[251] | 687 | cls = self.level.get_class(choice['classname'])
|
---|
[355] | 688 | edit_dlg = self._edit_class(choice['classname'], cls, choice)
|
---|
| 689 | if edit_dlg is not None:
|
---|
| 690 | edited = edit_dlg.get_data()
|
---|
| 691 | if self.level.try_new_object(choice["classname"],
|
---|
| 692 | self.level._game_objects,
|
---|
| 693 | edited, choice):
|
---|
| 694 | edit_dlg.cleanup()
|
---|
[225] | 695 | elif res == 'Delete':
|
---|
[239] | 696 | self.level._game_objects.remove(choice)
|
---|
| 697 | self.level.reset_objs()
|
---|
[225] | 698 |
|
---|
| 699 | def edit_enemies(self):
|
---|
| 700 | edit_box = self._make_edit_dialog(self.level._enemies)
|
---|
| 701 | res = edit_box.present()
|
---|
[236] | 702 | choice = edit_box.get_selection()
|
---|
| 703 | if choice is None:
|
---|
| 704 | return
|
---|
[225] | 705 | if res == 'OK':
|
---|
[251] | 706 | cls = self.level.get_class(choice['classname'], ne)
|
---|
[355] | 707 | edit_dlg = self._edit_class(choice['classname'], cls, choice)
|
---|
| 708 | if edit_dlg is not None:
|
---|
| 709 | edited = edit_dlg.get_data()
|
---|
| 710 | if self.level.try_new_object(choice["classname"],
|
---|
| 711 | self.level._enemies,
|
---|
| 712 | edited, choice):
|
---|
| 713 | edit_dlg.cleanup()
|
---|
[225] | 714 | elif res == 'Delete':
|
---|
[239] | 715 | self.level._enemies.remove(choice)
|
---|
| 716 | self.level.reset_objs()
|
---|
[225] | 717 |
|
---|
[236] | 718 | def _make_choice_dialog(self, classes):
|
---|
| 719 | # Dialog to hold the editor
|
---|
| 720 | data = []
|
---|
| 721 | for cls_name, cls in classes:
|
---|
| 722 | data.append({"classname": cls_name, "class": cls})
|
---|
[239] | 723 | choice_box = Dialog()
|
---|
| 724 | choice_box.rect = pygame.rect.Rect(0, 0, 700, 500)
|
---|
[236] | 725 | table = ObjectTable(data)
|
---|
[239] | 726 | choice_box.add(table)
|
---|
[236] | 727 | buttons = []
|
---|
| 728 | for text in ['OK', 'Cancel']:
|
---|
[239] | 729 | but = Button(text, action=lambda x=text: choice_box.dismiss(x))
|
---|
[236] | 730 | buttons.append(but)
|
---|
| 731 | row = Row(buttons)
|
---|
[251] | 732 | row.rect = pygame.rect.Rect(250, 450, 700, 50)
|
---|
[239] | 733 | choice_box.add(row)
|
---|
| 734 | choice_box.get_selection = lambda: table.get_selection()
|
---|
| 735 | return choice_box
|
---|
[236] | 736 |
|
---|
| 737 | def add_game_object(self):
|
---|
[251] | 738 | classes = ngo.get_editable_game_objects()
|
---|
| 739 | choose = self._make_choice_dialog(classes)
|
---|
| 740 | res = choose.present()
|
---|
| 741 | choice = choose.get_selection()
|
---|
| 742 | if res == 'OK' and choice is not None:
|
---|
| 743 | classname = choice['classname']
|
---|
| 744 | cls = choice['class']
|
---|
[355] | 745 | edit_dlg = self._edit_class(classname, cls, None)
|
---|
| 746 | if edit_dlg is not None:
|
---|
| 747 | new_cls = edit_dlg.get_data()
|
---|
| 748 | if self.level.try_new_object(classname,
|
---|
| 749 | self.level._game_objects,
|
---|
| 750 | new_cls, None):
|
---|
| 751 | edit_dlg.cleanup()
|
---|
[251] | 752 |
|
---|
| 753 | def add_enemy(self):
|
---|
| 754 | classes = ne.get_editable_enemies()
|
---|
[236] | 755 | choose = self._make_choice_dialog(classes)
|
---|
| 756 | res = choose.present()
|
---|
| 757 | choice = choose.get_selection()
|
---|
| 758 | if res == 'OK' and choice is not None:
|
---|
[251] | 759 | classname = choice['classname']
|
---|
| 760 | cls = choice['class']
|
---|
[355] | 761 | edit_dlg = self._edit_class(classname, cls, None)
|
---|
| 762 | if edit_dlg is not None:
|
---|
| 763 | new_cls = edit_dlg.get_data()
|
---|
| 764 | if self.level.try_new_object(classname, self.level._enemies,
|
---|
| 765 | new_cls, None):
|
---|
| 766 | edit_dlg.cleanup()
|
---|
[236] | 767 |
|
---|
[251] | 768 | def add_puzzler(self):
|
---|
| 769 | classes = np.get_editable_puzzlers()
|
---|
[236] | 770 | choose = self._make_choice_dialog(classes)
|
---|
| 771 | res = choose.present()
|
---|
| 772 | choice = choose.get_selection()
|
---|
| 773 | if res == 'OK' and choice is not None:
|
---|
[251] | 774 | classname = choice['classname']
|
---|
| 775 | cls = choice['class']
|
---|
[355] | 776 | edit_dlg = self._edit_class(classname, cls, None)
|
---|
| 777 | if edit_dlg is not None:
|
---|
| 778 | new_cls = edit_dlg.get_data()
|
---|
| 779 | if self.level.try_new_object(classname,
|
---|
| 780 | self.level._game_objects,
|
---|
| 781 | new_cls, None):
|
---|
| 782 | edit_dlg.cleanup()
|
---|
[236] | 783 |
|
---|
[108] | 784 |
|
---|
[280] | 785 | class HighLightButton(Button):
|
---|
| 786 | """Button with highlight support"""
|
---|
| 787 | def __init__(self, text, parent, **kwds):
|
---|
| 788 | super(HighLightButton, self).__init__(text, **kwds)
|
---|
| 789 | self._parent = parent
|
---|
| 790 |
|
---|
| 791 | def highlight(self):
|
---|
| 792 | self.border_color = pygame.color.Color('red')
|
---|
| 793 |
|
---|
| 794 | def reset(self):
|
---|
| 795 | self.border_color = self.fg_color
|
---|
| 796 |
|
---|
| 797 |
|
---|
| 798 | class PolyButton(HighLightButton):
|
---|
[115] | 799 | """Button for coosing the correct polygon"""
|
---|
| 800 |
|
---|
[280] | 801 | def __init__(self, index, level_widget, parent):
|
---|
[115] | 802 | if index is not None:
|
---|
| 803 | text = "Draw: %s" % index
|
---|
| 804 | else:
|
---|
| 805 | text = 'Exit Draw Mode'
|
---|
[280] | 806 | super(PolyButton, self).__init__(text, parent)
|
---|
[115] | 807 | self.index = index
|
---|
| 808 | self.level_widget = level_widget
|
---|
| 809 |
|
---|
| 810 | def action(self):
|
---|
| 811 | self.level_widget.change_poly(self.index)
|
---|
[280] | 812 | self._parent.reset_lit_buttons()
|
---|
| 813 | if self.index is not None:
|
---|
| 814 | self.highlight()
|
---|
[115] | 815 |
|
---|
| 816 |
|
---|
[108] | 817 | class EditorApp(RootWidget):
|
---|
| 818 |
|
---|
| 819 | def __init__(self, level, surface):
|
---|
| 820 | super(EditorApp, self).__init__(surface)
|
---|
| 821 | self.level = level
|
---|
[280] | 822 | self.level_widget = LevelWidget(self.level, self)
|
---|
[108] | 823 | self.add(self.level_widget)
|
---|
| 824 |
|
---|
[220] | 825 | self._dMenus = {}
|
---|
| 826 |
|
---|
[280] | 827 | self._light_buttons = []
|
---|
| 828 |
|
---|
[220] | 829 | self._make_draw_menu()
|
---|
| 830 | self._make_objects_menu()
|
---|
| 831 |
|
---|
| 832 | self._menu_mode = 'drawing'
|
---|
| 833 | self._populate_menu()
|
---|
| 834 |
|
---|
| 835 | def _make_draw_menu(self):
|
---|
| 836 | widgets = []
|
---|
| 837 |
|
---|
[115] | 838 | # Add poly buttons
|
---|
| 839 | y = 15
|
---|
| 840 | for poly in range(1, 7):
|
---|
[280] | 841 | but = PolyButton(poly, self.level_widget, self)
|
---|
[115] | 842 | but.rect = pygame.rect.Rect(0, 0, MENU_WIDTH // 2 - MENU_PAD,
|
---|
| 843 | MENU_BUTTON_HEIGHT)
|
---|
| 844 | if poly % 2:
|
---|
| 845 | but.rect.move_ip(MENU_LEFT, y)
|
---|
| 846 | else:
|
---|
| 847 | but.rect.move_ip(MENU_LEFT + MENU_WIDTH // 2 - MENU_HALF_PAD,
|
---|
| 848 | y)
|
---|
| 849 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
[280] | 850 | self._light_buttons.append(but)
|
---|
[220] | 851 | widgets.append(but)
|
---|
[198] | 852 |
|
---|
[280] | 853 | end_poly_but = PolyButton(None, self.level_widget, self)
|
---|
[220] | 854 | end_poly_but.rect = BUTTON_RECT.copy()
|
---|
[115] | 855 | end_poly_but.rect.move_ip(MENU_LEFT, y)
|
---|
[220] | 856 | widgets.append(end_poly_but)
|
---|
[115] | 857 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 858 |
|
---|
[407] | 859 | self.move_point_but = HighLightButton("Move Point", self,
|
---|
| 860 | action=self.move_point)
|
---|
| 861 | self.move_point_but.rect = BUTTON_RECT.copy()
|
---|
| 862 | self.move_point_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 863 | widgets.append(self.move_point_but)
|
---|
| 864 | self._light_buttons.append(self.move_point_but)
|
---|
| 865 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 866 |
|
---|
[280] | 867 | self.draw_line_but = HighLightButton("Draw interior wall", self,
|
---|
| 868 | action=self.set_line_mode)
|
---|
| 869 | self.draw_line_but.rect = BUTTON_RECT.copy()
|
---|
| 870 | self.draw_line_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 871 | widgets.append(self.draw_line_but)
|
---|
| 872 | self._light_buttons.append(self.draw_line_but)
|
---|
[204] | 873 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 874 |
|
---|
[117] | 875 | fill_but = Button('Fill exterior', action=self.level_widget.set_filled)
|
---|
[220] | 876 | fill_but.rect = BUTTON_RECT.copy()
|
---|
[117] | 877 | fill_but.rect.move_ip(MENU_LEFT, y)
|
---|
[220] | 878 | widgets.append(fill_but)
|
---|
[117] | 879 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 880 |
|
---|
[135] | 881 | close_poly_but = Button('Close Polygon',
|
---|
| 882 | action=self.level_widget.close_poly)
|
---|
[220] | 883 | close_poly_but.rect = BUTTON_RECT.copy()
|
---|
[135] | 884 | close_poly_but.rect.move_ip(MENU_LEFT, y)
|
---|
[220] | 885 | widgets.append(close_poly_but)
|
---|
[135] | 886 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 887 |
|
---|
[198] | 888 | white = pygame.color.Color("white")
|
---|
| 889 | self.show_objs = CheckBox(fg_color=white)
|
---|
[220] | 890 | self.show_objs.rect = CHECK_RECT.copy()
|
---|
[198] | 891 | self.show_objs.rect.move_ip(MENU_LEFT, y)
|
---|
| 892 | label = Label("Show Objects", fg_color=white)
|
---|
| 893 | label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
|
---|
[220] | 894 | widgets.append(self.show_objs)
|
---|
| 895 | widgets.append(label)
|
---|
[198] | 896 | y += label.rect.height + MENU_PAD
|
---|
| 897 |
|
---|
| 898 | self.show_enemies = CheckBox(fg_color=white)
|
---|
[220] | 899 | self.show_enemies.rect = CHECK_RECT.copy()
|
---|
[198] | 900 | self.show_enemies.rect.move_ip(MENU_LEFT, y)
|
---|
| 901 | label = Label("Show enemy start pos", fg_color=white)
|
---|
| 902 | label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
|
---|
[220] | 903 | widgets.append(self.show_enemies)
|
---|
| 904 | widgets.append(label)
|
---|
[198] | 905 | y += label.rect.height + MENU_PAD
|
---|
| 906 |
|
---|
[236] | 907 | y += MENU_PAD
|
---|
[220] | 908 | switch_but = Button('Switch to Objects', action=self.switch_to_objects)
|
---|
| 909 | switch_but.rect = BUTTON_RECT.copy()
|
---|
| 910 | switch_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 911 | widgets.append(switch_but)
|
---|
| 912 | y += switch_but.rect.height + MENU_PAD
|
---|
| 913 |
|
---|
[236] | 914 | save_but = Button('Save Level', action=self.save)
|
---|
| 915 | save_but.rect = BUTTON_RECT.copy()
|
---|
| 916 | save_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 917 | widgets.append(save_but)
|
---|
| 918 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 919 |
|
---|
[330] | 920 | y = SCREEN[1] - MENU_BUTTON_HEIGHT - MENU_PAD
|
---|
[108] | 921 | quit_but = Button('Quit', action=self.quit)
|
---|
[220] | 922 | quit_but.rect = BUTTON_RECT.copy()
|
---|
[115] | 923 | quit_but.rect.move_ip(MENU_LEFT, y)
|
---|
[220] | 924 | widgets.append(quit_but)
|
---|
| 925 |
|
---|
| 926 | self._dMenus['drawing'] = widgets
|
---|
| 927 |
|
---|
| 928 | def _make_objects_menu(self):
|
---|
| 929 | widgets = []
|
---|
| 930 |
|
---|
| 931 | # Add poly buttons
|
---|
| 932 | y = 15
|
---|
| 933 |
|
---|
[225] | 934 | edit_objs_but = Button('Edit Objects',
|
---|
| 935 | action=self.level_widget.edit_objects)
|
---|
| 936 | edit_objs_but.rect = BUTTON_RECT.copy()
|
---|
| 937 | edit_objs_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 938 | widgets.append(edit_objs_but)
|
---|
| 939 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 940 |
|
---|
| 941 | edir_enemies_but = Button('Edit Enemies',
|
---|
| 942 | action=self.level_widget.edit_enemies)
|
---|
| 943 | edir_enemies_but.rect = BUTTON_RECT.copy()
|
---|
| 944 | edir_enemies_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 945 | widgets.append(edir_enemies_but)
|
---|
| 946 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 947 |
|
---|
[236] | 948 | add_obj_but = Button('Add Game Object',
|
---|
| 949 | action=self.level_widget.add_game_object)
|
---|
| 950 | add_obj_but.rect = BUTTON_RECT.copy()
|
---|
| 951 | add_obj_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 952 | widgets.append(add_obj_but)
|
---|
| 953 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 954 |
|
---|
| 955 | add_puzzle_but = Button('Add Puzzler',
|
---|
| 956 | action=self.level_widget.add_puzzler)
|
---|
| 957 | add_puzzle_but.rect = BUTTON_RECT.copy()
|
---|
| 958 | add_puzzle_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 959 | widgets.append(add_puzzle_but)
|
---|
| 960 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 961 |
|
---|
| 962 | add_enemy_but = Button('Add Enemy',
|
---|
| 963 | action=self.level_widget.add_enemy)
|
---|
| 964 | add_enemy_but.rect = BUTTON_RECT.copy()
|
---|
| 965 | add_enemy_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 966 | widgets.append(add_enemy_but)
|
---|
| 967 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 968 |
|
---|
| 969 | y += MENU_PAD
|
---|
[280] | 970 | self.sel_mode_but = HighLightButton('Select Object', self,
|
---|
| 971 | action=self.sel_mode)
|
---|
| 972 | self.sel_mode_but.rect = BUTTON_RECT.copy()
|
---|
| 973 | self.sel_mode_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 974 | widgets.append(self.sel_mode_but)
|
---|
| 975 | self._light_buttons.append(self.sel_mode_but)
|
---|
[275] | 976 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 977 |
|
---|
| 978 | y += MENU_PAD
|
---|
[236] | 979 | switch_but = Button('Switch to Drawing', action=self.switch_to_draw)
|
---|
| 980 | switch_but.rect = BUTTON_RECT.copy()
|
---|
| 981 | switch_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 982 | widgets.append(switch_but)
|
---|
| 983 | y += switch_but.rect.height + MENU_PAD
|
---|
| 984 |
|
---|
[220] | 985 | save_but = Button('Save Level', action=self.save)
|
---|
| 986 | save_but.rect = BUTTON_RECT.copy()
|
---|
| 987 | save_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 988 | widgets.append(save_but)
|
---|
| 989 | y += MENU_BUTTON_HEIGHT + MENU_PAD
|
---|
| 990 |
|
---|
[330] | 991 | y = SCREEN[1] - MENU_BUTTON_HEIGHT - MENU_PAD
|
---|
[220] | 992 | quit_but = Button('Quit', action=self.quit)
|
---|
| 993 | quit_but.rect = BUTTON_RECT.copy()
|
---|
| 994 | quit_but.rect.move_ip(MENU_LEFT, y)
|
---|
| 995 | widgets.append(quit_but)
|
---|
| 996 |
|
---|
| 997 | self._dMenus['objects'] = widgets
|
---|
[108] | 998 |
|
---|
| 999 | def key_down(self, ev):
|
---|
| 1000 | if ev.key == pgl.K_ESCAPE:
|
---|
| 1001 | self.quit()
|
---|
[116] | 1002 | elif ev.key == pgl.K_s:
|
---|
| 1003 | self.save()
|
---|
[108] | 1004 | else:
|
---|
| 1005 | self.level_widget.key_down(ev)
|
---|
[51] | 1006 |
|
---|
[116] | 1007 | def save(self):
|
---|
[122] | 1008 | closed, messages = self.level.all_closed()
|
---|
| 1009 | if closed:
|
---|
| 1010 | self.level.save()
|
---|
| 1011 | # display success
|
---|
| 1012 | alert("Level %s saved successfully." % self.level.name)
|
---|
| 1013 | else:
|
---|
| 1014 | # display errors
|
---|
| 1015 | alert("Failed to save level.\n\n%s" % '\n'.join(messages))
|
---|
[116] | 1016 |
|
---|
[220] | 1017 | def switch_to_draw(self):
|
---|
| 1018 | if self._menu_mode != 'drawing':
|
---|
| 1019 | self._clear_menu()
|
---|
| 1020 | self._menu_mode = 'drawing'
|
---|
| 1021 | self._populate_menu()
|
---|
| 1022 |
|
---|
| 1023 | def switch_to_objects(self):
|
---|
| 1024 | if self._menu_mode != 'objects':
|
---|
| 1025 | self._clear_menu()
|
---|
| 1026 | self._menu_mode = 'objects'
|
---|
| 1027 | self._populate_menu()
|
---|
| 1028 |
|
---|
| 1029 | def _clear_menu(self):
|
---|
| 1030 | for widget in self._dMenus[self._menu_mode]:
|
---|
| 1031 | self.remove(widget)
|
---|
| 1032 |
|
---|
[280] | 1033 | def reset_lit_buttons(self):
|
---|
| 1034 | for but in self._light_buttons:
|
---|
| 1035 | but.reset()
|
---|
| 1036 |
|
---|
[220] | 1037 | def _populate_menu(self):
|
---|
| 1038 | self.level_widget.change_poly(None)
|
---|
[275] | 1039 | self.level_widget.sel_mode = False
|
---|
[220] | 1040 | for widget in self._dMenus[self._menu_mode]:
|
---|
| 1041 | self.add(widget)
|
---|
| 1042 | self.invalidate()
|
---|
| 1043 |
|
---|
[280] | 1044 | def set_line_mode(self):
|
---|
| 1045 | self.level_widget.line_mode()
|
---|
| 1046 | self.draw_line_but.highlight()
|
---|
| 1047 |
|
---|
[275] | 1048 | def sel_mode(self):
|
---|
| 1049 | self.level_widget.sel_mode = not self.level_widget.sel_mode
|
---|
[280] | 1050 | if self.level_widget.sel_mode:
|
---|
| 1051 | self.sel_mode_but.highlight()
|
---|
| 1052 | else:
|
---|
| 1053 | self.sel_mode_but.reset()
|
---|
[275] | 1054 |
|
---|
[109] | 1055 | def mouse_move(self, ev):
|
---|
| 1056 | self.level_widget.mouse_move(ev)
|
---|
| 1057 |
|
---|
[407] | 1058 | def move_point(self):
|
---|
| 1059 | self.level_widget.set_move_mode()
|
---|
| 1060 | self.move_point_but.highlight()
|
---|
| 1061 |
|
---|
[199] | 1062 | def draw(self, surface):
|
---|
| 1063 | # Update checkbox state
|
---|
[223] | 1064 | if self._menu_mode == 'drawing':
|
---|
| 1065 | self.level_widget.set_objects(self.show_objs.value)
|
---|
| 1066 | self.level_widget.set_enemies(self.show_enemies.value)
|
---|
| 1067 | else:
|
---|
| 1068 | self.level_widget.set_objects(True)
|
---|
| 1069 | self.level_widget.set_enemies(True)
|
---|
[199] | 1070 | super(EditorApp, self).draw(surface)
|
---|
| 1071 |
|
---|
[51] | 1072 |
|
---|
| 1073 | if __name__ == "__main__":
|
---|
[195] | 1074 | if len(sys.argv) not in [2, 4]:
|
---|
[51] | 1075 | print 'Please supply a levelname or levelname and level size'
|
---|
| 1076 | sys.exit()
|
---|
[199] | 1077 | # Need to ensure we have defaults for rendering
|
---|
| 1078 | parse_args([])
|
---|
[51] | 1079 | pygame.display.init()
|
---|
| 1080 | pygame.font.init()
|
---|
[108] | 1081 | pygame.display.set_mode((SCREEN[0] + MENU_WIDTH, SCREEN[1]),
|
---|
[109] | 1082 | pgl.SWSURFACE)
|
---|
[195] | 1083 | if len(sys.argv) == 2:
|
---|
| 1084 | level = EditorLevel(sys.argv[1])
|
---|
| 1085 | level.load(pymunk.Space())
|
---|
| 1086 | elif len(sys.argv) == 4:
|
---|
| 1087 | level = EditorLevel(sys.argv[1], int(sys.argv[2]), int(sys.argv[3]))
|
---|
[51] | 1088 | pygame.display.set_caption('Nagslang Area Editor')
|
---|
[108] | 1089 | pygame.key.set_repeat(200, 100)
|
---|
| 1090 | app = EditorApp(level, pygame.display.get_surface())
|
---|
| 1091 | app.run()
|
---|