Mercurial > fife-parpg
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/demos/rio_de_hola/scripts/world.py Mon Jan 11 23:34:52 2010 +0000 @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- +from fife import fife +import math, random +from fife.extensions import pychan +from fife.extensions.pychan import widgets + +from scripts.common.eventlistenerbase import EventListenerBase +from fife.extensions.loaders import loadMapFile +from fife.extensions.savers import saveMapFile +from agents.hero import Hero +from agents.girl import Girl +from agents.cloud import Cloud +from agents.beekeeper import Beekeeper +from agents.agent import create_anonymous_agents +from settings import Setting + +TDS = Setting() + +class MapListener(fife.MapChangeListener): + def __init__(self, map): + fife.MapChangeListener.__init__(self) + map.addChangeListener(self) + + def onMapChanged(self, map, changedLayers): + return + print "Changes on map ", map.getId() + for layer in map.getLayers(): + print layer.getId() + print " ", ["%s, %x" % (i.getObject().getId(), i.getChangeInfo()) for i in layer.getChangedInstances()] + + def onLayerCreate(self, map, layer): + pass + + def onLayerDelete(self, map, layer): + pass + + +class World(EventListenerBase): + """ + The world! + + This class handles: + setup of map view (cameras ...) + loading the map + GUI for right clicks + handles mouse/key events which aren't handled by the GUI. + ( by inheriting from EventlistenerBase ) + + That's obviously too much, and should get factored out. + """ + def __init__(self, engine): + super(World, self).__init__(engine, regMouse=True, regKeys=True) + self.engine = engine + self.eventmanager = engine.getEventManager() + self.model = engine.getModel() + self.view = self.engine.getView() + self.filename = '' + self.pump_ctr = 0 # for testing purposis + self.ctrldown = False + self.instancemenu = None + self.instance_to_agent = {} + self.dynamic_widgets = {} + + def show_instancemenu(self, clickpoint, instance): + """ + Build and show a popupmenu for an instance that the player + clicked on. The available actions are dynamically added to + the menu (and mapped to the onXYZ functions). + """ + if instance.getFifeId() == self.hero.agent.getFifeId(): + return + + # Create the popup. + self.build_instancemenu() + self.instancemenu.clickpoint = clickpoint + self.instancemenu.instance = instance + + # Add the buttons according to circumstances. + self.instancemenu.addChild(self.dynamic_widgets['inspectButton']) + target_distance = self.hero.agent.getLocationRef().getLayerDistanceTo(instance.getLocationRef()) + if target_distance > 3.0: + self.instancemenu.addChild(self.dynamic_widgets['moveButton']) + else: + if self.instance_to_agent.has_key(instance.getFifeId()): + self.instancemenu.addChild(self.dynamic_widgets['talkButton']) + self.instancemenu.addChild(self.dynamic_widgets['kickButton']) + # And show it :) + self.instancemenu.position = (clickpoint.x, clickpoint.y) + self.instancemenu.show() + + def build_instancemenu(self): + """ + Just loads the menu from an XML file + and hooks the events up. + The buttons are removed and later re-added if appropiate. + """ + self.hide_instancemenu() + dynamicbuttons = ('moveButton', 'talkButton', 'kickButton', 'inspectButton') + self.instancemenu = pychan.loadXML('gui/instancemenu.xml') + self.instancemenu.mapEvents({ + 'moveButton' : self.onMoveButtonPress, + 'talkButton' : self.onTalkButtonPress, + 'kickButton' : self.onKickButtonPress, + 'inspectButton' : self.onInspectButtonPress, + }) + for btn in dynamicbuttons: + self.dynamic_widgets[btn] = self.instancemenu.findChild(name=btn) + self.instancemenu.removeAllChildren() + + def hide_instancemenu(self): + if self.instancemenu: + self.instancemenu.hide() + + def reset(self): + """ + Clear the agent information and reset the moving secondary camera state. + """ + self.map, self.agentlayer = None, None + self.cameras = {} + self.hero, self.girl, self.clouds, self.beekeepers = None, None, [], [] + self.cur_cam2_x, self.initial_cam2_x, self.cam2_scrolling_right = 0, 0, True + self.target_rotation = 0 + self.instance_to_agent = {} + + def load(self, filename): + """ + Load a xml map and setup agents and cameras. + """ + self.filename = filename + self.reset() + self.map = loadMapFile(filename, self.engine) + self.maplistener = MapListener(self.map) + + self.initAgents() + self.initCameras() + + def initAgents(self): + """ + Setup agents. + + For this techdemo we have a very simple 'active things on the map' model, + which is called agents. All rio maps will have a separate layer for them. + + Note that we keep a mapping from map instances (C++ model of stuff on the map) + to the python agents for later reference. + """ + self.agentlayer = self.map.getLayer('TechdemoMapGroundObjectLayer') + self.hero = Hero(self.model, 'PC', self.agentlayer) + self.instance_to_agent[self.hero.agent.getFifeId()] = self.hero + self.hero.start() + + self.girl = Girl(self.model, 'NPC:girl', self.agentlayer) + self.instance_to_agent[self.girl.agent.getFifeId()] = self.girl + self.girl.start() + + self.beekeepers = create_anonymous_agents(self.model, 'beekeeper', self.agentlayer, Beekeeper) + for beekeeper in self.beekeepers: + self.instance_to_agent[beekeeper.agent.getFifeId()] = beekeeper + beekeeper.start() + + # Clouds are currently defunct. + cloudlayer = self.map.getLayer('TechdemoMapTileLayer') + self.clouds = create_anonymous_agents(self.model, 'Cloud', cloudlayer, Cloud) + for cloud in self.clouds: + cloud.start(0.1, 0.05) + + + def initCameras(self): + """ + Before we can actually see something on screen we have to specify the render setup. + This is done through Camera objects which offer a viewport on the map. + + For this techdemo two cameras are used. One follows the hero(!) via 'attach' + the other one scrolls around a bit (see the pump function). + """ + camera_prefix = self.filename.rpartition('.')[0] # Remove file extension + camera_prefix = camera_prefix.rpartition('/')[2] # Remove path + camera_prefix += '_' + + for cam in self.view.getCameras(): + camera_id = cam.getId().replace(camera_prefix, '') + self.cameras[camera_id] = cam + + self.cameras['main'].attach(self.hero.agent) + + self.view.resetRenderers() + # Floating text renderers currntly only support one font. + # ... so we set that up. + # You'll se that for our demo we use a image font, so we have to specify the font glyphs + # for that one. + renderer = fife.FloatingTextRenderer.getInstance(self.cameras['main']) + textfont = self.engine.getGuiManager().createFont('fonts/rpgfont.png', 0, str(TDS.readSetting("FontGlyphs", strip=False))); + renderer.changeDefaultFont(textfont) + + # The small camera shouldn't be cluttered by the 'humm di dums' of our hero. + # So we disable the renderer simply by setting its font to None. + renderer = fife.FloatingTextRenderer.getInstance(self.cameras['small']) + renderer.changeDefaultFont(None) + + # The following renderers are used for debugging. + # Note that by default ( that is after calling View.resetRenderers or Camera.resetRenderers ) + # renderers will be handed all layers. That's handled here. + renderer = self.cameras['main'].getRenderer('CoordinateRenderer') + renderer.clearActiveLayers() + renderer.addActiveLayer(self.map.getLayer(str(TDS.readSetting("CoordinateLayerName")))) + + renderer = self.cameras['main'].getRenderer('QuadTreeRenderer') + renderer.setEnabled(True) + renderer.clearActiveLayers() + if str(TDS.readSetting("QuadTreeLayerName")): + renderer.addActiveLayer(self.map.getLayer(str(TDS.readSetting("QuadTreeLayerName")))) + + # Set up the second camera + # NOTE: We need to explicitly call setLocation, there's a bit of a messup in the Camera code. + self.cameras['small'].setLocation(self.hero.agent.getLocation()) + self.cameras['small'].attach(self.girl.agent) + + self.target_rotation = self.cameras['main'].getRotation() + + + def save(self, filename): + saveMapFile(filename, self.engine, self.map) + + def getInstancesAt(self, clickpoint): + """ + Query the main camera for instances on our active(agent) layer. + """ + return self.cameras['main'].getMatchingInstances(clickpoint, self.agentlayer) + + def getLocationAt(self, clickpoint): + """ + Query the main camera for the Map location (on the agent layer) + that a screen point refers to. + """ + target_mapcoord = self.cameras['main'].toMapCoordinates(clickpoint, False) + target_mapcoord.z = 0 + location = fife.Location(self.agentlayer) + location.setMapCoordinates(target_mapcoord) + return location + + def keyPressed(self, evt): + keyval = evt.getKey().getValue() + keystr = evt.getKey().getAsString().lower() + if keystr == 't': + r = self.cameras['main'].getRenderer('GridRenderer') + r.setEnabled(not r.isEnabled()) + elif keystr == 'c': + r = self.cameras['main'].getRenderer('CoordinateRenderer') + r.setEnabled(not r.isEnabled()) + elif keystr == 's': + c = self.cameras['small'] + c.setEnabled(not c.isEnabled()) + elif keystr == 'r': + self.model.deleteMaps() + self.view.clearCameras() + self.load(self.filename) + elif keystr == 'o': + self.target_rotation = (self.target_rotation + 90) % 360 + elif keyval in (fife.Key.LEFT_CONTROL, fife.Key.RIGHT_CONTROL): + self.ctrldown = True + + def keyReleased(self, evt): + keyval = evt.getKey().getValue() + if keyval in (fife.Key.LEFT_CONTROL, fife.Key.RIGHT_CONTROL): + self.ctrldown = False + + def mouseWheelMovedUp(self, evt): + if self.ctrldown: + self.cameras['main'].setZoom(self.cameras['main'].getZoom() * 1.05) + + def mouseWheelMovedDown(self, evt): + if self.ctrldown: + self.cameras['main'].setZoom(self.cameras['main'].getZoom() / 1.05) + + def changeRotation(self): + """ + Smoothly change the main cameras rotation until + the current target rotation is reached. + """ + currot = self.cameras['main'].getRotation() + if self.target_rotation != currot: + self.cameras['main'].setRotation((currot + 5) % 360) + + def mousePressed(self, evt): + if evt.isConsumedByWidgets(): + return + + clickpoint = fife.ScreenPoint(evt.getX(), evt.getY()) + if (evt.getButton() == fife.MouseEvent.LEFT): + self.hide_instancemenu() + self.hero.run( self.getLocationAt(clickpoint) ) + + if (evt.getButton() == fife.MouseEvent.RIGHT): + instances = self.getInstancesAt(clickpoint) + print "selected instances on agent layer: ", [i.getObject().getId() for i in instances] + if instances: + self.show_instancemenu(clickpoint, instances[0]) + + def mouseMoved(self, evt): + renderer = fife.InstanceRenderer.getInstance(self.cameras['main']) + renderer.removeAllOutlines() + + pt = fife.ScreenPoint(evt.getX(), evt.getY()) + instances = self.getInstancesAt(pt); + for i in instances: + if i.getObject().getId() in ('girl', 'beekeeper'): + renderer.addOutlined(i, 173, 255, 47, 2) + + def onConsoleCommand(self, command): + result = '' + try: + result = str(eval(command)) + except Exception, e: + result = str(e) + return result + + # Callbacks from the popupmenu + def onMoveButtonPress(self): + self.hide_instancemenu() + self.hero.run(self.instancemenu.instance.getLocationRef()) + + def onTalkButtonPress(self): + self.hide_instancemenu() + instance = self.instancemenu.instance + self.hero.talk(instance.getLocationRef()) + if instance.getObject().getId() == 'beekeeper': + beekeeperTexts = TDS.readSetting("beekeeperTexts", type='list', text=True) + instance.say(random.choice(beekeeperTexts), 5000) + if instance.getObject().getId() == 'girl': + girlTexts = TDS.readSetting("girlTexts", type='list', text=True) + instance.say(random.choice(girlTexts), 5000) + + def onKickButtonPress(self): + self.hide_instancemenu() + self.hero.kick(self.instancemenu.instance.getLocationRef()) + self.instancemenu.instance.say('Hey!', 1000) + + def onInspectButtonPress(self): + self.hide_instancemenu() + inst = self.instancemenu.instance + saytext = ['Engine told me that this instance has'] + if inst.getId(): + saytext.append(' name %s,' % inst.getId()) + saytext.append(' ID %s and' % inst.getFifeId()) + saytext.append(' object name %s' % inst.getObject().getId()) + self.hero.agent.say('\n'.join(saytext), 3500) + + def pump(self): + """ + Called every frame. + """ + + self.changeRotation() + self.pump_ctr += 1