comparison demos/rio_de_hola/scripts/world.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 70697641fca3
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
1 # -*- coding: utf-8 -*-
2 from fife import fife
3 import math, random
4 from fife.extensions import pychan
5 from fife.extensions.pychan import widgets
6
7 from scripts.common.eventlistenerbase import EventListenerBase
8 from fife.extensions.loaders import loadMapFile
9 from fife.extensions.savers import saveMapFile
10 from agents.hero import Hero
11 from agents.girl import Girl
12 from agents.cloud import Cloud
13 from agents.beekeeper import Beekeeper
14 from agents.agent import create_anonymous_agents
15 from settings import Setting
16
17 TDS = Setting()
18
19 class MapListener(fife.MapChangeListener):
20 def __init__(self, map):
21 fife.MapChangeListener.__init__(self)
22 map.addChangeListener(self)
23
24 def onMapChanged(self, map, changedLayers):
25 return
26 print "Changes on map ", map.getId()
27 for layer in map.getLayers():
28 print layer.getId()
29 print " ", ["%s, %x" % (i.getObject().getId(), i.getChangeInfo()) for i in layer.getChangedInstances()]
30
31 def onLayerCreate(self, map, layer):
32 pass
33
34 def onLayerDelete(self, map, layer):
35 pass
36
37
38 class World(EventListenerBase):
39 """
40 The world!
41
42 This class handles:
43 setup of map view (cameras ...)
44 loading the map
45 GUI for right clicks
46 handles mouse/key events which aren't handled by the GUI.
47 ( by inheriting from EventlistenerBase )
48
49 That's obviously too much, and should get factored out.
50 """
51 def __init__(self, engine):
52 super(World, self).__init__(engine, regMouse=True, regKeys=True)
53 self.engine = engine
54 self.eventmanager = engine.getEventManager()
55 self.model = engine.getModel()
56 self.view = self.engine.getView()
57 self.filename = ''
58 self.pump_ctr = 0 # for testing purposis
59 self.ctrldown = False
60 self.instancemenu = None
61 self.instance_to_agent = {}
62 self.dynamic_widgets = {}
63
64 def show_instancemenu(self, clickpoint, instance):
65 """
66 Build and show a popupmenu for an instance that the player
67 clicked on. The available actions are dynamically added to
68 the menu (and mapped to the onXYZ functions).
69 """
70 if instance.getFifeId() == self.hero.agent.getFifeId():
71 return
72
73 # Create the popup.
74 self.build_instancemenu()
75 self.instancemenu.clickpoint = clickpoint
76 self.instancemenu.instance = instance
77
78 # Add the buttons according to circumstances.
79 self.instancemenu.addChild(self.dynamic_widgets['inspectButton'])
80 target_distance = self.hero.agent.getLocationRef().getLayerDistanceTo(instance.getLocationRef())
81 if target_distance > 3.0:
82 self.instancemenu.addChild(self.dynamic_widgets['moveButton'])
83 else:
84 if self.instance_to_agent.has_key(instance.getFifeId()):
85 self.instancemenu.addChild(self.dynamic_widgets['talkButton'])
86 self.instancemenu.addChild(self.dynamic_widgets['kickButton'])
87 # And show it :)
88 self.instancemenu.position = (clickpoint.x, clickpoint.y)
89 self.instancemenu.show()
90
91 def build_instancemenu(self):
92 """
93 Just loads the menu from an XML file
94 and hooks the events up.
95 The buttons are removed and later re-added if appropiate.
96 """
97 self.hide_instancemenu()
98 dynamicbuttons = ('moveButton', 'talkButton', 'kickButton', 'inspectButton')
99 self.instancemenu = pychan.loadXML('gui/instancemenu.xml')
100 self.instancemenu.mapEvents({
101 'moveButton' : self.onMoveButtonPress,
102 'talkButton' : self.onTalkButtonPress,
103 'kickButton' : self.onKickButtonPress,
104 'inspectButton' : self.onInspectButtonPress,
105 })
106 for btn in dynamicbuttons:
107 self.dynamic_widgets[btn] = self.instancemenu.findChild(name=btn)
108 self.instancemenu.removeAllChildren()
109
110 def hide_instancemenu(self):
111 if self.instancemenu:
112 self.instancemenu.hide()
113
114 def reset(self):
115 """
116 Clear the agent information and reset the moving secondary camera state.
117 """
118 self.map, self.agentlayer = None, None
119 self.cameras = {}
120 self.hero, self.girl, self.clouds, self.beekeepers = None, None, [], []
121 self.cur_cam2_x, self.initial_cam2_x, self.cam2_scrolling_right = 0, 0, True
122 self.target_rotation = 0
123 self.instance_to_agent = {}
124
125 def load(self, filename):
126 """
127 Load a xml map and setup agents and cameras.
128 """
129 self.filename = filename
130 self.reset()
131 self.map = loadMapFile(filename, self.engine)
132 self.maplistener = MapListener(self.map)
133
134 self.initAgents()
135 self.initCameras()
136
137 def initAgents(self):
138 """
139 Setup agents.
140
141 For this techdemo we have a very simple 'active things on the map' model,
142 which is called agents. All rio maps will have a separate layer for them.
143
144 Note that we keep a mapping from map instances (C++ model of stuff on the map)
145 to the python agents for later reference.
146 """
147 self.agentlayer = self.map.getLayer('TechdemoMapGroundObjectLayer')
148 self.hero = Hero(self.model, 'PC', self.agentlayer)
149 self.instance_to_agent[self.hero.agent.getFifeId()] = self.hero
150 self.hero.start()
151
152 self.girl = Girl(self.model, 'NPC:girl', self.agentlayer)
153 self.instance_to_agent[self.girl.agent.getFifeId()] = self.girl
154 self.girl.start()
155
156 self.beekeepers = create_anonymous_agents(self.model, 'beekeeper', self.agentlayer, Beekeeper)
157 for beekeeper in self.beekeepers:
158 self.instance_to_agent[beekeeper.agent.getFifeId()] = beekeeper
159 beekeeper.start()
160
161 # Clouds are currently defunct.
162 cloudlayer = self.map.getLayer('TechdemoMapTileLayer')
163 self.clouds = create_anonymous_agents(self.model, 'Cloud', cloudlayer, Cloud)
164 for cloud in self.clouds:
165 cloud.start(0.1, 0.05)
166
167
168 def initCameras(self):
169 """
170 Before we can actually see something on screen we have to specify the render setup.
171 This is done through Camera objects which offer a viewport on the map.
172
173 For this techdemo two cameras are used. One follows the hero(!) via 'attach'
174 the other one scrolls around a bit (see the pump function).
175 """
176 camera_prefix = self.filename.rpartition('.')[0] # Remove file extension
177 camera_prefix = camera_prefix.rpartition('/')[2] # Remove path
178 camera_prefix += '_'
179
180 for cam in self.view.getCameras():
181 camera_id = cam.getId().replace(camera_prefix, '')
182 self.cameras[camera_id] = cam
183
184 self.cameras['main'].attach(self.hero.agent)
185
186 self.view.resetRenderers()
187 # Floating text renderers currntly only support one font.
188 # ... so we set that up.
189 # You'll se that for our demo we use a image font, so we have to specify the font glyphs
190 # for that one.
191 renderer = fife.FloatingTextRenderer.getInstance(self.cameras['main'])
192 textfont = self.engine.getGuiManager().createFont('fonts/rpgfont.png', 0, str(TDS.readSetting("FontGlyphs", strip=False)));
193 renderer.changeDefaultFont(textfont)
194
195 # The small camera shouldn't be cluttered by the 'humm di dums' of our hero.
196 # So we disable the renderer simply by setting its font to None.
197 renderer = fife.FloatingTextRenderer.getInstance(self.cameras['small'])
198 renderer.changeDefaultFont(None)
199
200 # The following renderers are used for debugging.
201 # Note that by default ( that is after calling View.resetRenderers or Camera.resetRenderers )
202 # renderers will be handed all layers. That's handled here.
203 renderer = self.cameras['main'].getRenderer('CoordinateRenderer')
204 renderer.clearActiveLayers()
205 renderer.addActiveLayer(self.map.getLayer(str(TDS.readSetting("CoordinateLayerName"))))
206
207 renderer = self.cameras['main'].getRenderer('QuadTreeRenderer')
208 renderer.setEnabled(True)
209 renderer.clearActiveLayers()
210 if str(TDS.readSetting("QuadTreeLayerName")):
211 renderer.addActiveLayer(self.map.getLayer(str(TDS.readSetting("QuadTreeLayerName"))))
212
213 # Set up the second camera
214 # NOTE: We need to explicitly call setLocation, there's a bit of a messup in the Camera code.
215 self.cameras['small'].setLocation(self.hero.agent.getLocation())
216 self.cameras['small'].attach(self.girl.agent)
217
218 self.target_rotation = self.cameras['main'].getRotation()
219
220
221 def save(self, filename):
222 saveMapFile(filename, self.engine, self.map)
223
224 def getInstancesAt(self, clickpoint):
225 """
226 Query the main camera for instances on our active(agent) layer.
227 """
228 return self.cameras['main'].getMatchingInstances(clickpoint, self.agentlayer)
229
230 def getLocationAt(self, clickpoint):
231 """
232 Query the main camera for the Map location (on the agent layer)
233 that a screen point refers to.
234 """
235 target_mapcoord = self.cameras['main'].toMapCoordinates(clickpoint, False)
236 target_mapcoord.z = 0
237 location = fife.Location(self.agentlayer)
238 location.setMapCoordinates(target_mapcoord)
239 return location
240
241 def keyPressed(self, evt):
242 keyval = evt.getKey().getValue()
243 keystr = evt.getKey().getAsString().lower()
244 if keystr == 't':
245 r = self.cameras['main'].getRenderer('GridRenderer')
246 r.setEnabled(not r.isEnabled())
247 elif keystr == 'c':
248 r = self.cameras['main'].getRenderer('CoordinateRenderer')
249 r.setEnabled(not r.isEnabled())
250 elif keystr == 's':
251 c = self.cameras['small']
252 c.setEnabled(not c.isEnabled())
253 elif keystr == 'r':
254 self.model.deleteMaps()
255 self.view.clearCameras()
256 self.load(self.filename)
257 elif keystr == 'o':
258 self.target_rotation = (self.target_rotation + 90) % 360
259 elif keyval in (fife.Key.LEFT_CONTROL, fife.Key.RIGHT_CONTROL):
260 self.ctrldown = True
261
262 def keyReleased(self, evt):
263 keyval = evt.getKey().getValue()
264 if keyval in (fife.Key.LEFT_CONTROL, fife.Key.RIGHT_CONTROL):
265 self.ctrldown = False
266
267 def mouseWheelMovedUp(self, evt):
268 if self.ctrldown:
269 self.cameras['main'].setZoom(self.cameras['main'].getZoom() * 1.05)
270
271 def mouseWheelMovedDown(self, evt):
272 if self.ctrldown:
273 self.cameras['main'].setZoom(self.cameras['main'].getZoom() / 1.05)
274
275 def changeRotation(self):
276 """
277 Smoothly change the main cameras rotation until
278 the current target rotation is reached.
279 """
280 currot = self.cameras['main'].getRotation()
281 if self.target_rotation != currot:
282 self.cameras['main'].setRotation((currot + 5) % 360)
283
284 def mousePressed(self, evt):
285 if evt.isConsumedByWidgets():
286 return
287
288 clickpoint = fife.ScreenPoint(evt.getX(), evt.getY())
289 if (evt.getButton() == fife.MouseEvent.LEFT):
290 self.hide_instancemenu()
291 self.hero.run( self.getLocationAt(clickpoint) )
292
293 if (evt.getButton() == fife.MouseEvent.RIGHT):
294 instances = self.getInstancesAt(clickpoint)
295 print "selected instances on agent layer: ", [i.getObject().getId() for i in instances]
296 if instances:
297 self.show_instancemenu(clickpoint, instances[0])
298
299 def mouseMoved(self, evt):
300 renderer = fife.InstanceRenderer.getInstance(self.cameras['main'])
301 renderer.removeAllOutlines()
302
303 pt = fife.ScreenPoint(evt.getX(), evt.getY())
304 instances = self.getInstancesAt(pt);
305 for i in instances:
306 if i.getObject().getId() in ('girl', 'beekeeper'):
307 renderer.addOutlined(i, 173, 255, 47, 2)
308
309 def onConsoleCommand(self, command):
310 result = ''
311 try:
312 result = str(eval(command))
313 except Exception, e:
314 result = str(e)
315 return result
316
317 # Callbacks from the popupmenu
318 def onMoveButtonPress(self):
319 self.hide_instancemenu()
320 self.hero.run(self.instancemenu.instance.getLocationRef())
321
322 def onTalkButtonPress(self):
323 self.hide_instancemenu()
324 instance = self.instancemenu.instance
325 self.hero.talk(instance.getLocationRef())
326 if instance.getObject().getId() == 'beekeeper':
327 beekeeperTexts = TDS.readSetting("beekeeperTexts", type='list', text=True)
328 instance.say(random.choice(beekeeperTexts), 5000)
329 if instance.getObject().getId() == 'girl':
330 girlTexts = TDS.readSetting("girlTexts", type='list', text=True)
331 instance.say(random.choice(girlTexts), 5000)
332
333 def onKickButtonPress(self):
334 self.hide_instancemenu()
335 self.hero.kick(self.instancemenu.instance.getLocationRef())
336 self.instancemenu.instance.say('Hey!', 1000)
337
338 def onInspectButtonPress(self):
339 self.hide_instancemenu()
340 inst = self.instancemenu.instance
341 saytext = ['Engine told me that this instance has']
342 if inst.getId():
343 saytext.append(' name %s,' % inst.getId())
344 saytext.append(' ID %s and' % inst.getFifeId())
345 saytext.append(' object name %s' % inst.getObject().getId())
346 self.hero.agent.say('\n'.join(saytext), 3500)
347
348 def pump(self):
349 """
350 Called every frame.
351 """
352
353 self.changeRotation()
354 self.pump_ctr += 1