Mercurial > fife-parpg
comparison clients/editor/plugins/mapeditor.py @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children | 9d0a21184c13 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4a0efb7baf70 |
---|---|
1 # MapEditor is a plugin for Fifedit. It allows for selection and visual editing of maps. | |
2 # MapEditor must be pumped (see pump). | |
3 | |
4 import math | |
5 | |
6 import fife | |
7 import plugin | |
8 import pychan | |
9 import pychan.widgets as widgets | |
10 from pychan.tools import callbackWithArguments as cbwa | |
11 from selection import Selection, ClickSelection | |
12 from plugins.objectselector import ObjectSelector | |
13 | |
14 from pychan.manager import DEFAULT_STYLE | |
15 DEFAULT_STYLE['default']['base_color'] = fife.Color(85,128,151) | |
16 | |
17 states = ('NOTHING_LOADED', 'VIEWING', 'INSERTING', 'REMOVING', 'MOVING') | |
18 for s in states: | |
19 globals()[s] = s | |
20 NOT_INITIALIZED = -9999999 | |
21 | |
22 class EditorLogicError(Exception): | |
23 pass | |
24 | |
25 class MapSelection(object): | |
26 def __init__(self, onLayerSelect, onObjectSelect): | |
27 self._mapedit = None | |
28 self._onLayerSelect = onLayerSelect | |
29 self._onObjectSelect = onObjectSelect | |
30 | |
31 def show(self, map): | |
32 if not self._mapedit: | |
33 self._mapedit = pychan.loadXML('content/gui/mapeditor.xml') | |
34 self._mapedit.mapEvents({ | |
35 'layerButton' : self._onLayerSelect, | |
36 'objButton' : self._onObjectSelect, | |
37 'closeButton' : self.hide | |
38 }) | |
39 fields = self._mapedit.findChild(name='Properties') | |
40 # Clear previously added children | |
41 fields.removeChildren(*fields.children) | |
42 hbox = widgets.HBox() | |
43 fields.addChild(hbox) | |
44 label = widgets.Label(text='ID',min_size=(80,0)) | |
45 hbox.addChild(label) | |
46 field = widgets.TextField(text=map.getId(),min_size=(100,0)) | |
47 hbox.addChild(field) | |
48 self._mapedit.adaptLayout() | |
49 self._mapedit.show() | |
50 self._mapedit.x = 10 | |
51 self._mapedit.y = 580 | |
52 | |
53 def hide(self): | |
54 self._mapedit.hide() | |
55 | |
56 class Toolbar(object): | |
57 def __init__(self, onSelect, onMove, onInsert, onDelete, onBtnEnter, onBtnExit): | |
58 self._onSelect, self._onMove, self._onInsert, self._onDelete = onSelect, onMove, onInsert, onDelete | |
59 self.onBtnEnter, self.onBtnExit = onBtnEnter, onBtnExit | |
60 self._toolbar = None | |
61 | |
62 def show(self): | |
63 if not self._toolbar: | |
64 self._toolbar = pychan.loadXML('content/gui/tools.xml') | |
65 evtmap = { | |
66 'btnSelect' : self._onSelect, | |
67 'btnMove' : self._onMove, | |
68 'btnInsert' : self._onInsert, | |
69 'btnDelete' : self._onDelete | |
70 } | |
71 self._toolbar.mapEvents(evtmap) | |
72 for k in evtmap.keys(): | |
73 btn = self._toolbar.findChild(name=k) | |
74 btn.setEnterCallback(self.onBtnEnter) | |
75 btn.setExitCallback(self.onBtnExit) | |
76 | |
77 #self._toolbar.adaptLayout() | |
78 self._toolbar.show() | |
79 self._toolbar.x = 10 | |
80 self._toolbar.y = 50 | |
81 | |
82 def hide(self): | |
83 self._toolbar.hide() | |
84 | |
85 def _enableBtn(self, enabled, btn): | |
86 pass | |
87 | |
88 def enableInsert(self, enabled): | |
89 self._enableBtn(enabled, self._toolbar.findChild(name='btnInsert')) | |
90 | |
91 def enableDelete(self, enabled): | |
92 self._enableBtn(enabled, self._toolbar.findChild(name='btnDelete')) | |
93 | |
94 def enableSelect(self, enabled): | |
95 self._enableBtn(enabled, self._toolbar.findChild(name='btnSelect')) | |
96 | |
97 class StatusBar(object): | |
98 def __init__(self, screenw, screenh): | |
99 self._statusbar = pychan.loadXML('content/gui/statuspanel.xml') | |
100 self._statusbar.show() | |
101 height = 25 | |
102 self._statusbar.position = (0, screenh - height) | |
103 self._statusbar.size = (screenw, height) | |
104 self.statustxt = '' | |
105 self.lbl = self._statusbar.findChild(name='lblStatus') | |
106 | |
107 def setStatus(self, msg): | |
108 self.statustxt = msg | |
109 self.lbl.text = ' ' + msg | |
110 self.lbl.resizeToContent() | |
111 | |
112 def showTooltip(self, elem): | |
113 self.lbl.text = elem.helptext | |
114 self.lbl.resizeToContent() | |
115 | |
116 def hideTooltip(self, elem): | |
117 self.lbl.text = self.statustxt | |
118 self.lbl.resizeToContent() | |
119 | |
120 | |
121 class MapEditor(plugin.Plugin,fife.IMouseListener, fife.IKeyListener): | |
122 def __init__(self, engine): | |
123 self._engine = engine | |
124 eventmanager = self._engine.getEventManager() | |
125 eventmanager.setNonConsumableKeys([ | |
126 fife.Key.LEFT, | |
127 fife.Key.RIGHT, | |
128 fife.Key.UP, | |
129 fife.Key.DOWN]) | |
130 fife.IMouseListener.__init__(self) | |
131 eventmanager.addMouseListener(self) | |
132 fife.IKeyListener.__init__(self) | |
133 eventmanager.addKeyListener(self) | |
134 | |
135 # Fifedit plugin data | |
136 self.menu_items = { 'Select Map' : self._selectMap } | |
137 | |
138 self._camera = None # currently selected camera | |
139 self._map = None # currently selected map | |
140 self._layer = None # currently selected layer | |
141 self._object = None # currently selected object | |
142 self._selection = None # currently selected coordinates | |
143 self._instances = None # currently selected instances | |
144 | |
145 self._ctrldown = False | |
146 self._shiftdown = False | |
147 self._altdown = False | |
148 self._dragx = NOT_INITIALIZED | |
149 self._dragy = NOT_INITIALIZED | |
150 | |
151 self._mapselector = MapSelection(self._selectLayer, self._selectObject) | |
152 self._objectselector = None | |
153 rb = self._engine.getRenderBackend() | |
154 self._statusbar = StatusBar(rb.getWidth(), rb.getHeight()) | |
155 self._toolbar = Toolbar(cbwa(self._setMode, VIEWING), cbwa(self._setMode, MOVING), | |
156 cbwa(self._setMode, INSERTING), cbwa(self._setMode, REMOVING), | |
157 self._statusbar.showTooltip, self._statusbar.hideTooltip) | |
158 self._toolbar.show() | |
159 self._setMode(NOTHING_LOADED) | |
160 | |
161 def _assert(self, statement, msg): | |
162 if not statement: | |
163 print msg | |
164 raise EditorLogicError(msg) | |
165 | |
166 def _setMode(self, mode): | |
167 if (mode != NOTHING_LOADED) and (not self._camera): | |
168 self._statusbar.setStatus('Please load map first') | |
169 return | |
170 if (mode == INSERTING) and (not self._object): | |
171 self._statusbar.setStatus('Please select object first') | |
172 return | |
173 self._mode = mode | |
174 print "Entered mode " + mode | |
175 self._statusbar.setStatus(mode.replace('_', ' ').capitalize()) | |
176 | |
177 # gui for selecting a map | |
178 def _selectMap(self): | |
179 Selection([map.getId() for map in self._engine.getModel().getMaps()], self.editMap) | |
180 | |
181 def _selectDefaultCamera(self, map): | |
182 self._camera = None | |
183 | |
184 self._engine.getView().resetRenderers() | |
185 for cam in self._engine.getView().getCameras(): | |
186 cam.setEnabled(False) | |
187 | |
188 for cam in self._engine.getView().getCameras(): | |
189 if cam.getLocationRef().getMap().getId() == map.getId(): | |
190 rb = self._engine.getRenderBackend() | |
191 cam.setViewPort(fife.Rect(0, 0, rb.getScreenWidth(), rb.getScreenHeight())) | |
192 cam.setEnabled(True) | |
193 self._camera = cam | |
194 break | |
195 if not self._camera: | |
196 raise AttributeError('No cameras found associated with this map: ' + map.getId()) | |
197 | |
198 def editMap(self, mapid): | |
199 self._camera = None | |
200 self._map = None | |
201 self._layer = None | |
202 self._object = None | |
203 self._selection = None | |
204 self._instances = None | |
205 self._setMode(NOTHING_LOADED) | |
206 | |
207 self._map = self._engine.getModel().getMap(mapid) | |
208 if not self._map.getLayers(): | |
209 raise AttributeError('Editor error: map ' + self._map.getId() + ' has no layers. Cannot edit.') | |
210 | |
211 self._layer = self._map.getLayers()[0] | |
212 self._selectDefaultCamera(self._map) | |
213 self._setMode(VIEWING) | |
214 | |
215 self._mapselector.show(self._map) | |
216 | |
217 def _selectLayer(self): | |
218 Selection([layer.getId() for layer in self._map.getLayers()], self._editLayer) | |
219 | |
220 def _editLayer(self, layerid): | |
221 self._layer = None | |
222 layers = [l for l in self._map.getLayers() if l.getId() == layerid] | |
223 self._assert(len(layers) == 1, 'Layer amount != 1') | |
224 self._layer = layers[0] | |
225 | |
226 def _selectObject(self): | |
227 if not self._objectselector: | |
228 self._objectselector = ObjectSelector(self._engine, self._map, self._editObject) | |
229 self._objectselector.show() | |
230 | |
231 def _editObject(self, object): | |
232 self._object = object | |
233 | |
234 def _selectCell(self, screenx, screeny, preciseCoords=False): | |
235 self._assert(self._camera, 'No camera bind yet, cannot select any cell') | |
236 | |
237 self._selection = self._camera.toMapCoordinates(fife.ScreenPoint(screenx, screeny), False) | |
238 self._selection.z = 0 | |
239 loc = fife.Location(self._layer) | |
240 if preciseCoords: | |
241 self._selection = self._layer.getCellGrid().toExactLayerCoordinates(self._selection) | |
242 loc.setExactLayerCoordinates(self._selection) | |
243 else: | |
244 self._selection = self._layer.getCellGrid().toLayerCoordinates(self._selection) | |
245 loc.setLayerCoordinates(self._selection) | |
246 fife.CellSelectionRenderer.getInstance(self._camera).selectLocation(loc) | |
247 return loc | |
248 | |
249 def _getInstancesFromSelection(self, top_only): | |
250 self._assert(self._layer, 'No layer assigned in _getInstancesFromSelection') | |
251 self._assert(self._selection, 'No selection assigned in _getInstancesFromSelection') | |
252 self._assert(self._camera, 'No camera assigned in _getInstancesFromSelection') | |
253 | |
254 loc = fife.Location(self._layer) | |
255 if type(self._selection) == fife.ExactModelCoordinate: | |
256 loc.setExactLayerCoordinates(self._selection) | |
257 else: | |
258 loc.setLayerCoordinates(self._selection) | |
259 instances = self._camera.getMatchingInstances(loc) | |
260 if top_only and (len(instances) > 0): | |
261 instances = [instances[0]] | |
262 return instances | |
263 | |
264 def _placeInstance(self): | |
265 mname = '_placeInstance' | |
266 self._assert(self._object, 'No object assigned in %s' % mname) | |
267 self._assert(self._selection, 'No selection assigned in %s' % mname) | |
268 self._assert(self._layer, 'No layer assigned in %s' % mname) | |
269 self._assert(self._mode == INSERTING, 'No mode is not INSERTING in %s (is instead %s)' % (mname, str(self._mode))) | |
270 | |
271 # don't place repeat instances | |
272 for i in self._getInstancesFromSelection(False): | |
273 if i.getObject().getId() == self._object.getId(): | |
274 print 'Warning: attempt to place duplicate instance of object %s. Ignoring request.' % self._object.getId() | |
275 return | |
276 | |
277 inst = self._layer.createInstance(self._object, self._selection) | |
278 fife.InstanceVisual.create(inst) | |
279 | |
280 def _removeInstances(self): | |
281 mname = '_removeInstances' | |
282 self._assert(self._selection, 'No selection assigned in %s' % mname) | |
283 self._assert(self._layer, 'No layer assigned in %s' % mname) | |
284 self._assert(self._mode == REMOVING, 'Mode is not REMOVING in %s (is instead %s)' % (mname, str(self._mode))) | |
285 | |
286 for i in self._getInstancesFromSelection(top_only=True): | |
287 print "deleting " + str(i) | |
288 self._layer.deleteInstance(i) | |
289 | |
290 def _moveInstances(self): | |
291 mname = '_removeInstances' | |
292 self._assert(self._selection, 'No selection assigned in %s' % mname) | |
293 self._assert(self._layer, 'No layer assigned in %s' % mname) | |
294 self._assert(self._mode == MOVING, 'Mode is not MOVING in %s (is instead %s)' % (mname, str(self._mode))) | |
295 | |
296 loc = fife.Location(self._layer) | |
297 if self._shiftdown: | |
298 loc.setExactLayerCoordinates(self._selection) | |
299 else: | |
300 loc.setLayerCoordinates(self._selection) | |
301 for i in self._instances: | |
302 i.setLocation(loc) | |
303 | |
304 def _rotateInstances(self): | |
305 mname = '_rotateInstances' | |
306 self._assert(self._selection, 'No selection assigned in %s' % mname) | |
307 self._assert(self._layer, 'No layer assigned in %s' % mname) | |
308 | |
309 for i in self._getInstancesFromSelection(top_only=True): | |
310 i.setRotation((i.getRotation() + 90) % 360) | |
311 ## Surprisingly, the following "snap-to-rotation" code is actually incorrect. Object | |
312 ## rotation is independent of the camera, whereas the choice of an actual rotation image | |
313 ## depends very much on how the camera is situated. For example, suppose an object has | |
314 ## rotations defined for 45,135,225,315. And suppose the camera position results in an | |
315 ## effective 60 degree rotation. If the object is given a rotation of 0, then the (correct) | |
316 ## final rotation value of 45 (which is closest to 60 = 0 + 60) will be chosen. If we try | |
317 ## to snap to the closest value to 0 (45), then an incorrect final rotation value will be | |
318 ## chosen: 135, which is closest to 105 = 45 + 60. --jwt | |
319 # ovis = i.getObject().get2dGfxVisual() | |
320 # curUsedAngle = ovis.getClosestMatchingAngle(i.getRotation()) | |
321 # angles = ovis.getStaticImageAngles() | |
322 # if angles: | |
323 # ind = list(angles).index(curUsedAngle) | |
324 # if ind == (len(angles) - 1): | |
325 # ind = 0 | |
326 # else: | |
327 # ind += 1 | |
328 # i.setRotation(angles[ind]) | |
329 # else: | |
330 # print "rotation not supported for this instance" | |
331 | |
332 def changeRotation(self): | |
333 currot = self._camera.getRotation() | |
334 self._camera.setRotation((currot + 90) % 360) | |
335 | |
336 def _moveCamera(self, screen_x, screen_y): | |
337 coords = self._camera.getLocationRef().getMapCoordinates() | |
338 z = self._camera.getZoom() | |
339 r = self._camera.getRotation() | |
340 if screen_x: | |
341 coords.x -= screen_x / z * math.cos(r / 180.0 * math.pi) / 100; | |
342 coords.y -= screen_x / z * math.sin(r / 180.0 * math.pi) / 100; | |
343 if screen_y: | |
344 coords.x -= screen_y / z * math.sin(-r / 180.0 * math.pi) / 100; | |
345 coords.y -= screen_y / z * math.cos(-r / 180.0 * math.pi) / 100; | |
346 coords = self._camera.getLocationRef().setMapCoordinates(coords) | |
347 self._camera.refresh() | |
348 | |
349 def mousePressed(self, evt): | |
350 if self._ctrldown: | |
351 if evt.getButton() == fife.MouseEvent.LEFT: | |
352 self._dragx = evt.getX() | |
353 self._dragy = evt.getY() | |
354 else: | |
355 if self._camera: | |
356 self._selectCell(evt.getX(), evt.getY(), self._shiftdown) | |
357 if self._mode == VIEWING: | |
358 self._instances = self._getInstancesFromSelection(top_only=True) | |
359 elif self._mode == INSERTING: | |
360 self._placeInstance() | |
361 elif self._mode == REMOVING: | |
362 self._removeInstances() | |
363 elif self._mode == MOVING: | |
364 self._instances = self._getInstancesFromSelection(top_only=True) | |
365 else: | |
366 self._setMode(self._mode) # refresh status | |
367 | |
368 def mouseDragged(self, evt): | |
369 if self._ctrldown: | |
370 if (self._dragx != NOT_INITIALIZED) and (self._dragy != NOT_INITIALIZED): | |
371 self._moveCamera(evt.getX() - self._dragx, evt.getY() - self._dragy) | |
372 self._dragx = evt.getX() | |
373 self._dragy = evt.getY() | |
374 else: | |
375 if self._mode == INSERTING: | |
376 self._selectCell(evt.getX(), evt.getY()) | |
377 self._placeInstance() | |
378 elif self._mode == REMOVING: | |
379 self._selectCell(evt.getX(), evt.getY()) | |
380 self._removeInstances() | |
381 elif self._mode == MOVING and self._instances: | |
382 self._selectCell(evt.getX(), evt.getY(), self._shiftdown) | |
383 self._moveInstances() | |
384 | |
385 def mouseReleased(self, evt): | |
386 self._dragx = NOT_INITIALIZED | |
387 self._dragy = NOT_INITIALIZED | |
388 | |
389 def mouseMoved(self, evt): | |
390 pass | |
391 def mouseEntered(self, evt): | |
392 pass | |
393 def mouseExited(self, evt): | |
394 pass | |
395 def mouseClicked(self, evt): | |
396 pass | |
397 | |
398 def mouseWheelMovedUp(self, evt): | |
399 if self._ctrldown and self._camera: | |
400 self._camera.setZoom(self._camera.getZoom() * 1.05) | |
401 | |
402 def mouseWheelMovedDown(self, evt): | |
403 if self._ctrldown and self._camera: | |
404 self._camera.setZoom(self._camera.getZoom() / 1.05) | |
405 | |
406 | |
407 def keyPressed(self, evt): | |
408 keyval = evt.getKey().getValue() | |
409 keystr = evt.getKey().getAsString().lower() | |
410 | |
411 if keyval == fife.Key.LEFT: | |
412 self._moveCamera(50, 0) | |
413 elif keyval == fife.Key.RIGHT: | |
414 self._moveCamera(-50, 0) | |
415 elif keyval == fife.Key.UP: | |
416 self._moveCamera(0, 50) | |
417 elif keyval == fife.Key.DOWN: | |
418 self._moveCamera(0, -50) | |
419 elif keyval in (fife.Key.LEFT_CONTROL, fife.Key.RIGHT_CONTROL): | |
420 self._ctrldown = True | |
421 elif keyval in (fife.Key.LEFT_SHIFT, fife.Key.RIGHT_SHIFT): | |
422 self._shiftdown = True | |
423 elif keyval in (fife.Key.LEFT_ALT, fife.Key.RIGHT_ALT): | |
424 self._altdown = True | |
425 | |
426 elif keyval == fife.Key.INSERT: | |
427 if self._mode != INSERTING: | |
428 self._setMode(INSERTING) | |
429 else: | |
430 self._setMode(VIEWING) | |
431 | |
432 elif keyval == fife.Key.DELETE_KEY: | |
433 if self._mode != REMOVING: | |
434 self._setMode(REMOVING) | |
435 else: | |
436 self._setMode(VIEWING) | |
437 | |
438 elif keystr == 'm': | |
439 if self._mode != MOVING: | |
440 self._setMode(MOVING) | |
441 else: | |
442 self._setMode(VIEWING) | |
443 | |
444 elif keystr == 't': | |
445 gridrenderer = self._camera.getRenderer('GridRenderer') | |
446 gridrenderer.setEnabled(not gridrenderer.isEnabled()) | |
447 | |
448 elif keystr == 'b': | |
449 blockrenderer = self._camera.getRenderer('BlockingInfoRenderer') | |
450 blockrenderer.setEnabled(not blockrenderer.isEnabled()) | |
451 | |
452 elif keystr == 'r': | |
453 if self._selection: | |
454 self._rotateInstances() | |
455 | |
456 elif keystr == 'o': | |
457 self.changeRotation() | |
458 | |
459 def keyReleased(self, evt): | |
460 keyval = evt.getKey().getValue() | |
461 if keyval in (fife.Key.LEFT_CONTROL, fife.Key.RIGHT_CONTROL): | |
462 self._ctrldown = False | |
463 elif keyval in (fife.Key.LEFT_SHIFT, fife.Key.RIGHT_SHIFT): | |
464 self._shiftdown = False | |
465 elif keyval in (fife.Key.LEFT_ALT, fife.Key.RIGHT_ALT): | |
466 self._altdown = False | |
467 | |
468 |