comparison tools/editor/scripts/mapcontroller.py @ 378:64738befdf3b

bringing in the changes from the build_system_rework branch in preparation for the 0.3.0 release. This commit will require the Jan2010 devkit. Clients will also need to be modified to the new way to import fife.
author vtchill@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 11 Jan 2010 23:34:52 +0000
parents
children 9d94f4676d17
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
1 # -*- coding: utf-8 -*-
2
3 # ####################################################################
4 # Copyright (C) 2005-2009 by the FIFE team
5 # http://www.fifengine.de
6 # This file is part of FIFE.
7 #
8 # FIFE is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the
20 # Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 # ####################################################################
23
24 import editor
25 import pdb
26
27 import math
28
29 from fife import fife
30 import editor
31 import events
32 import undomanager
33
34 from fife.extensions.pychan.tools import callbackWithArguments as cbwa
35
36 class MapController(object):
37 """ MapController provides an interface for editing maps """
38 def __init__(self, map):
39
40 self._editor = editor.getEditor()
41 self._engine = self._editor.getEngine()
42
43 self._camera = None # currently selected camera
44 self._layer = None # currently selected layer
45 self._selection = [] # currently selected cells
46 self._map = None
47 self._undo = False
48 self._undomanager = undomanager.UndoManager()
49 undomanager.preUndo.connect(self._startUndo, sender=self._undomanager)
50 undomanager.preRedo.connect(self._startUndo, sender=self._undomanager)
51 undomanager.postUndo.connect(self._endUndo, sender=self._undomanager)
52 undomanager.postRedo.connect(self._endUndo, sender=self._undomanager)
53 self.debug = False
54
55 self.overwriteInstances = True # Remove instances on cell before placing new instance
56
57 if map is not None:
58 self.setMap(map.getId())
59
60 def cleanUp(self):
61 undomanager.preUndo.disconnect(self._startUndo, sender=self._undomanager)
62 undomanager.preRedo.disconnect(self._startUndo, sender=self._undomanager)
63 undomanager.postUndo.disconnect(self._endUndo, sender=self._undomanager)
64 undomanager.postRedo.disconnect(self._endUndo, sender=self._undomanager)
65 self._undomanager.clear()
66
67 self._editor = None
68 self._engine = None
69 self._map = None
70 self._selection = []
71 self._layer = None
72 self._camera = None
73
74 def setMap(self, mapid):
75 """ Set the map to be edited """
76 self._camera = None
77 self._map = None
78 self._layer = None
79 self._selection = []
80
81 self._map = self._engine.getModel().getMap(mapid)
82 if not self._map.getLayers():
83 raise AttributeError('Editor error: map ' + self._map.getId() + ' has no layers. Cannot edit.')
84
85 for cam in self._engine.getView().getCameras():
86 if cam.getLocationRef().getMap().getId() == self._map.getId():
87 self._camera = cam
88 break
89
90 self._layer = self._map.getLayers()[0]
91
92 def selectLayer(self, layerid):
93 """ Select layer to be edited """
94 self.deselectSelection()
95 self._layer = None
96 layers = [l for l in self._map.getLayers() if l.getId() == layerid]
97 if len(layers) == 1:
98 self._layer = layers[0]
99
100 def deselectSelection(self):
101 """ Deselects all selected cells """
102 if not self._camera:
103 if self.debug: print 'No camera bind yet, cannot select any cell'
104 return
105 self._selection = []
106 fife.CellSelectionRenderer.getInstance(self._camera).reset()
107
108 def clearSelection(self):
109 """ Removes all instances on selected cells """
110 instances = self.getInstancesFromSelection()
111 self._undomanager.startGroup("Cleared selection")
112 self.removeInstances(instances)
113 self._undomanager.endGroup()
114
115 def fillSelection(self, object):
116 """ Adds an instance of object on each selected cell """
117 self._undomanager.startGroup("Filled selection")
118 for loc in self._selection:
119 self.placeInstance(loc.getLayerCoordinates(), object)
120 self._undomanager.endGroup()
121
122 def selectCell(self, screenx, screeny):
123 """ Selects a cell at a position on screen """
124 if not self._camera:
125 if self.debug: print 'No camera bind yet, cannot select any cell'
126 return
127 if not self._layer:
128 if self.debug: print 'No layer assigned in selectCell'
129 return
130
131 mapCoords = self._camera.toMapCoordinates(fife.ScreenPoint(screenx, screeny), False)
132 position = self._layer.getCellGrid().toLayerCoordinates(mapCoords)
133
134 loc = fife.Location(self._layer)
135 loc.setLayerCoordinates(position)
136
137 for i in self._selection:
138 if loc == i: return
139
140 self._selection.append( loc )
141 fife.CellSelectionRenderer.getInstance(self._camera).selectLocation(loc)
142
143 def deselectCell(self, screenx, screeny):
144 """ Deselects a cell at a position on screen """
145 if not self._camera:
146 if self.debug: print 'No camera bind yet, cannot select any cell'
147 return
148 if not self._layer:
149 if self.debug: print 'No layer assigned in selectCell'
150 return
151
152 mapCoords = self._camera.toMapCoordinates(fife.ScreenPoint(screenx, screeny), False)
153 position = self._layer.getCellGrid().toLayerCoordinates(mapCoords)
154
155 loc = fife.Location(self._layer)
156 loc.setLayerCoordinates(position)
157
158 for i in self._selection:
159 if loc == i:
160 self._selection.remove( loc )
161 fife.CellSelectionRenderer.getInstance(self._camera).deselectLocation(loc)
162 return
163
164
165 def getInstancesFromSelection(self):
166 """ Returns all instances in the selected cells """
167 instances = []
168
169 for loc in self._selection:
170 instances.extend(self.getInstancesFromPosition(loc.getLayerCoordinates()))
171
172 return instances
173
174 def getInstancesFromPosition(self, position):
175 """ Returns all instances on a specified position """
176 if not self._layer:
177 if self.debug: print 'No layer assigned in getInstancesFromPosition'
178 return
179 if not position:
180 if self.debug: print 'No position assigned in getInstancesFromPosition'
181 return
182
183 loc = fife.Location(self._layer)
184 if type(position) == fife.ExactModelCoordinate:
185 loc.setExactLayerCoordinates(position)
186 else:
187 loc.setLayerCoordinates(position)
188
189 instances = self._layer.getInstancesAt(loc)
190
191 return instances
192
193 def getUndoManager(self):
194 """ Returns undo manager """
195 return self._undomanager
196
197 def undo(self):
198 """ Undo one level """
199 self._undomanager.undo()
200
201 def redo(self):
202 """ Redo one level """
203 self._undomanager.redo()
204
205 def _startUndo(self):
206 """ Called before a undo operation is performed. Makes sure undo stack does not get corrupted """
207 self._undo = True
208
209 def _endUndo(self):
210 """ Called when a undo operation is done """
211 self._undo = False
212
213 def placeInstance(self, position, object):
214 """ Places an instance of object at position. Any existing instances on position are removed. """
215 mname = 'placeInstance'
216 if not object:
217 if self.debug: print 'No object assigned in %s' % mname
218 return
219 if not position:
220 if self.debug: print 'No position assigned in %s' % mname
221 return
222 if not self._layer:
223 if self.debug: print 'No layer assigned in %s' % mname
224 return
225
226 if self.debug: print 'Placing instance of ' + object.getId() + ' at ' + str(position)
227
228 # Remove instances from target position
229 if not self._undo:
230 instances = self.getInstancesFromPosition(position)
231 if len(instances) == 1:
232 # Check if the only instance at position has the same object
233 objectId = instances[0].getObject().getId()
234 objectNs = instances[0].getObject().getNamespace()
235 if objectId == object.getId() and objectNs == object.getNamespace():
236 if self.debug: print "Tried to place duplicate instance"
237 return
238
239 self._undomanager.startGroup("Placed instance")
240 self.removeInstances(instances)
241
242 inst = self._layer.createInstance(object, position)
243 fife.InstanceVisual.create(inst)
244
245 if not self._undo:
246 redocall = cbwa(self.placeInstance, position, object)
247 undocall = cbwa(self.removeInstanceOfObjectAt, position, object)
248 undoobject = undomanager.UndoObject(undocall, redocall, "Placed instance")
249 self._undomanager.addAction(undoobject)
250 self._undomanager.endGroup()
251
252 def removeInstanceOfObjectAt(self, position, object):
253 """ Removes the first instance of object it can find on position """
254 instances = self.getInstancesFromPosition(position)
255 for i in instances:
256 if i.getObject().getId() == object.getId() and i.getObject().getNamespace() == object.getNamespace():
257 self.removeInstances([i])
258 return
259
260 def removeInstances(self, instances):
261 """ Removes all provided instances """
262 mname = 'removeInstances'
263 if not instances:
264 if self.debug: print 'No instances assigned in %s' % mname
265 return
266
267 for i in instances:
268 if self.debug: print 'Deleting instance ' + i.getObject().getId() + ' at ' + str(i.getLocation().getExactLayerCoordinates())
269
270 if not self._undo:
271 object = i.getObject()
272 position = i.getLocation().getExactLayerCoordinates()
273 undocall = cbwa(self.placeInstance, position, object)
274 redocall = cbwa(self.removeInstanceOfObjectAt, position, object)
275 undoobject = undomanager.UndoObject(undocall, redocall, "Removed instance")
276 self._undomanager.addAction(undoobject)
277
278 self._layer.deleteInstance(i)
279
280 def moveInstances(self, instances, moveBy, exact=False):
281 """ Moves provided instances by moveBy. If exact is false, the instances are
282 snapped to closest cell. """
283 mname = 'moveInstances'
284 if not self._layer:
285 if self.debug: print 'No layer assigned in %s' % mname
286 return
287
288 if exact and type(moveBy) != fife.ExactModelCoordinate:
289 moveBy = fife.ExactModelCoordinate(float(moveBy.x), float(moveBy.y), float(moveBy.z))
290 elif exact is False and type(moveBy) != fife.ModelCoordinate:
291 moveBy = fife.ModelCoordinate(int(round(moveBy.x)), int(round(moveBy.y)), int(round(moveBy.z)))
292
293 for i in instances:
294 loc = i.getLocation()
295 f = i.getFacingLocation()
296 if exact:
297 newCoords = loc.getExactLayerCoordinates() + moveBy
298 loc.setExactLayerCoordinates(newCoords)
299 f.setExactLayerCoordinates(f.getExactLayerCoordinates() + moveBy)
300 else:
301 # Move instance and snap to closest cell
302 newCoords = loc.getLayerCoordinates() + moveBy
303 loc.setLayerCoordinates(newCoords)
304 f.setLayerCoordinates(f.getLayerCoordinates() + moveBy)
305 i.setLocation(loc)
306 i.setFacingLocation(f)
307
308 def rotateCounterClockwise(self):
309 """ Rotates map counterclockwise by 90 degrees """
310 currot = self._camera.getRotation()
311 self._camera.setRotation((currot + 270) % 360)
312
313 def rotateClockwise(self):
314 """ Rotates map clockwise by 90 degrees """
315 currot = self._camera.getRotation()
316 self._camera.setRotation((currot + 90) % 360)
317
318 def getZoom(self):
319 """ Returns camera zoom """
320 if not self._camera:
321 if self.debug: print 'No camera bind yet, cannot get zoom'
322 return 0
323 return self._camera.getZoom()
324
325 def setZoom(self, zoom):
326 """ Sets camera zoom """
327 if not self._camera:
328 if self.debug: print 'No camera bind yet, cannot get zoom'
329 return
330 self._camera.setZoom(zoom)
331
332 def moveCamera(self, screen_x, screen_y):
333 """ Move camera (scroll) by screen_x, screen_y """
334 if not self._camera:
335 return
336
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()