Exemple #1
0
def get_test_zone2():
    '''(NoneType) -> Zone'''
    test_zone = Zone()

    test_zone.load("test.txt")

    return test_zone
Exemple #2
0
class World(DirectObject):
    def __init__(self):
        self.last_mousex = 0
        self.last_mousey = 0

        self.zone = None
        self.zone_reload_name = None

        self.winprops = WindowProperties()

        # simple console output
        self.consoleNode = NodePath(PandaNode("console_root"))
        self.consoleNode.reparentTo(aspect2d)

        self.console_num_lines = 24
        self.console_cur_line = -1
        self.console_lines = []
        for i in range(0, self.console_num_lines):
            self.console_lines.append(
                OnscreenText(
                    text="",
                    style=1,
                    fg=(1, 1, 1, 1),
                    pos=(-1.3, 0.4 - i * 0.05),
                    align=TextNode.ALeft,
                    scale=0.035,
                    parent=self.consoleNode,
                )
            )

        # Configuration
        self.consoleOut("zonewalk v.%s loading configuration" % VERSION)
        self.configurator = Configurator(self)
        cfg = self.configurator.config
        resaveRes = False
        if "xres" in cfg:
            self.xres = int(cfg["xres"])
        else:
            self.xres = 1024
            resaveRes = True

        if "yres" in cfg:
            self.yres = int(cfg["yres"])
        else:
            self.yres = 768
            resaveRes = True

        if resaveRes:
            self.saveDefaultRes()

        self.xres_half = self.xres / 2
        self.yres_half = self.yres / 2
        self.mouse_accum = MouseAccume(lambda: (self.xres_half, self.yres_half))

        self.eyeHeight = 7.0
        self.rSpeed = 80
        self.flyMode = 1

        # application window setup
        base.win.setClearColor(Vec4(0, 0, 0, 1))
        self.winprops.setTitle("zonewalk")
        self.winprops.setSize(self.xres, self.yres)

        base.win.requestProperties(self.winprops)
        base.disableMouse()

        # network test stuff
        self.login_client = None
        if "testnet" in cfg:
            if cfg["testnet"] == "1":
                self.doLogin()

        # Post the instructions
        self.title = addTitle("zonewalk v." + VERSION)
        self.inst0 = addInstructions(0.95, "[FLYMODE][1]")
        self.inst1 = addInstructions(-0.95, "Camera control with WSAD/mouselook. Press K for hotkey list, ESC to exit.")
        self.inst2 = addInstructions(0.9, "Loc:")
        self.inst3 = addInstructions(0.85, "Hdg:")
        self.error_inst = addInstructions(0, "")
        self.kh = []

        self.campos = Point3(155.6, 41.2, 4.93)
        base.camera.setPos(self.campos)

        # Accept the application control keys: currently just esc to exit navgen
        self.accept("escape", self.exitGame)
        self.accept("window-event", self.resizeGame)

        # Create some lighting
        ambient_level = 0.6
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(ambient_level, ambient_level, ambient_level, 1.0))
        render.setLight(render.attachNewNode(ambientLight))

        direct_level = 0.8
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(0.0, 0.0, -1.0))
        directionalLight.setColor(Vec4(direct_level, direct_level, direct_level, 1))
        directionalLight.setSpecularColor(Vec4(direct_level, direct_level, direct_level, 1))
        render.setLight(render.attachNewNode(directionalLight))

        # create a point light that will follow our view point (the camera for now)
        # attenuation is set so that this point light has a torch like effect
        self.plight = PointLight("plight")
        self.plight.setColor(VBase4(0.8, 0.8, 0.8, 1.0))
        self.plight.setAttenuation(Point3(0.0, 0.0, 0.0002))

        self.plnp = base.camera.attachNewNode(self.plight)
        self.plnp.setPos(0, 0, 0)
        render.setLight(self.plnp)
        self.cam_light = 1

        self.keyMap = {
            "left": 0,
            "right": 0,
            "forward": 0,
            "backward": 0,
            "cam-left": 0,
            "cam-right": 0,
            "mouse3": 0,
            "flymode": 1,
        }

        # setup FOG
        self.fog_colour = (0.8, 0.8, 0.8, 1.0)
        self.linfog = Fog("A linear-mode Fog node")
        self.linfog.setColor(self.fog_colour)
        self.linfog.setLinearRange(700, 980)  # onset, opaque distances as params
        # linfog.setLinearFallback(45,160,320)
        base.camera.attachNewNode(self.linfog)
        render.setFog(self.linfog)
        self.fog = 1

        # camera control
        self.campos = Point3(0, 0, 0)
        self.camHeading = 0.0
        self.camPitch = 0.0
        base.camLens.setFov(65.0)
        base.camLens.setFar(1200)

        self.cam_speed = 0  # index into self.camp_speeds
        self.cam_speeds = [40.0, 80.0, 160.0, 320.0, 640.0]

        # Collision Detection for "WALKMODE"
        # We will detect the height of the terrain by creating a collision
        # ray and casting it downward toward the terrain.  The ray will start above the camera.
        # A ray may hit the terrain, or it may hit a rock or a tree.  If it
        # hits the terrain, we can detect the height.  If it hits anything
        # else, we rule that the move is illegal.

        self.cTrav = CollisionTraverser()
        self.camGroundRay = CollisionRay()
        self.camGroundRay.setOrigin(0.0, 0.0, 0.0)
        self.camGroundRay.setDirection(0, 0, -1)  # straight down
        self.camGroundCol = CollisionNode("camRay")
        self.camGroundCol.addSolid(self.camGroundRay)
        self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.camGroundCol.setIntoCollideMask(BitMask32.allOff())

        # attach the col node to the camCollider dummy node
        self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)
        self.camGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)

        # Uncomment this line to see the collision rays
        # self.camGroundColNp.show()

        # Uncomment this line to show a visual representation of the
        # collisions occuring
        # self.cTrav.showCollisions(render)

        # Add the spinCameraTask procedure to the task manager.
        # taskMgr.add(self.spinCameraTask, "SpinCameraTask")
        taskMgr.add(self.camTask, "camTask")

        self.toggleControls(1)

        # need to step the task manager once to make our fake console work
        taskMgr.step()

    # CONSOLE ---------------------------------------------------------------------
    def consoleScroll(self):
        for i in range(0, self.console_num_lines - 1):
            self.console_lines[i].setText(self.console_lines[i + 1].getText())

    def consoleOut(self, text):
        print text  # output to stdout/log too

        if self.console_cur_line == self.console_num_lines - 1:
            self.consoleScroll()
        elif self.console_cur_line < self.console_num_lines - 1:
            self.console_cur_line += 1

        self.console_lines[self.console_cur_line].setText(text)

        taskMgr.step()

    def consoleOn(self):
        self.consoleNode.show()

    def consoleOff(self):
        self.consoleNode.hide()

    # User controls -----------------------------------------------------------
    def toggleControls(self, on):
        if on == 1:
            self.accept("escape", self.exitGame)

            self.accept("1", self.setSpeed, ["speed", 0])
            self.accept("2", self.setSpeed, ["speed", 1])
            self.accept("3", self.setSpeed, ["speed", 2])
            self.accept("4", self.setSpeed, ["speed", 3])
            self.accept("5", self.setSpeed, ["speed", 4])

            self.accept("alt-f", self.fogToggle)
            self.accept("t", self.camLightToggle)
            self.accept("k", self.displayKeyHelp)
            self.accept("f", self.toggleFlymode)
            self.accept("l", self.reloadZone)
            self.accept("z", self.saveDefaultZone)
            self.accept("a", self.setKey, ["cam-left", 1])
            self.accept("d", self.setKey, ["cam-right", 1])
            self.accept("w", self.setKey, ["forward", 1])
            self.accept("mouse1", self.setKey, ["forward", 1])
            self.accept("mouse3", self.setKey, ["mouse3", 1])
            self.accept("s", self.setKey, ["backward", 1])

            self.accept("k-up", self.hideKeyHelp)
            self.accept("a-up", self.setKey, ["cam-left", 0])
            self.accept("d-up", self.setKey, ["cam-right", 0])
            self.accept("w-up", self.setKey, ["forward", 0])
            self.accept("mouse1-up", self.setKey, ["forward", 0])
            self.accept("mouse3-up", self.setKey, ["mouse3", 0])
            self.accept("s-up", self.setKey, ["backward", 0])
        else:
            messenger.clear()

    def setSpeed(self, key, value):
        self.cam_speed = value
        self.setFlymodeText()

    def fogToggle(self):
        if self.fog == 1:
            render.clearFog()
            base.camLens.setFar(100000)
            self.fog = 0
        else:
            render.setFog(self.linfog)
            base.camLens.setFar(1200)
            self.fog = 1

    def camLightToggle(self):
        if self.cam_light == 0:
            render.setLight(self.plnp)
            self.cam_light = 1
        else:
            render.clearLight(self.plnp)
            self.cam_light = 0

    def displayKeyHelp(self):
        self.kh = []
        msg = "HOTKEYS:"
        pos = 0.75
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "------------------"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "W: camera fwd, S: camera bck, A: rotate view left, D: rotate view right"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "1-5: set camera movement speed"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "F: toggle Flymode/Walkmode"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "L: load a zone"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "ALT-F: toggle FOG and FAR plane on/off"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = 'T: toggle additional camera "torch" light on/off'
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "Z: set currently loaded zone as new startup default"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )
        msg = "ESC: exit zonewalk"
        pos -= 0.05
        self.kh.append(
            OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), pos=(-0.5, pos), align=TextNode.ALeft, scale=0.04)
        )

    def hideKeyHelp(self):
        for n in self.kh:
            n.removeNode()

    def setFlymodeText(self):
        zname = ""
        if self.zone:
            zname = self.zone.name

        if self.flyMode == 0:
            self.inst0.setText("[WALKMODE][%i] %s" % (self.cam_speed + 1, zname))
        else:
            self.inst0.setText("[FLYMODE][%i] %s " % (self.cam_speed + 1, zname))

    def toggleFlymode(self):
        zname = ""
        if self.zone:
            zname = self.zone.name

        if self.flyMode == 0:
            self.flyMode = 1
        else:
            self.flyMode = 0

        self.setFlymodeText()

    # Define a procedure to move the camera.
    def spinCameraTask(self, task):
        angleDegrees = task.time * 6.0
        angleRadians = angleDegrees * (pi / 180.0)
        base.camera.setPos(20 * sin(angleRadians), -20.0 * cos(angleRadians), 3)
        base.camera.setHpr(angleDegrees, 0, 0)
        return task.cont

    def camTask(self, task):
        # query the mouse
        mouse_dx = 0
        mouse_dy = 0

        # if we have a mouse and the right button is depressed
        if base.mouseWatcherNode.hasMouse():
            if self.keyMap["mouse3"] != 0:
                self.mouse_accum.update()
            else:
                self.mouse_accum.reset()

        mouse_dx = self.mouse_accum.dx
        mouse_dy = self.mouse_accum.dy

        self.rXSpeed = fabs(self.mouse_accum.dx) * (self.cam_speed + 1) * max(5 * 1000 / self.xres, 3)
        self.rYSpeed = fabs(self.mouse_accum.dy) * (self.cam_speed + 1) * max(3 * 1000 / self.yres, 1)

        if self.keyMap["cam-left"] != 0 or mouse_dx < 0:
            if self.rSpeed < 160:
                self.rSpeed += 80 * globalClock.getDt()

            if mouse_dx != 0:
                self.camHeading += self.rXSpeed * globalClock.getDt()
            else:
                self.camHeading += self.rSpeed * globalClock.getDt()

            if self.camHeading > 360.0:
                self.camHeading = self.camHeading - 360.0
        elif self.keyMap["cam-right"] != 0 or mouse_dx > 0:
            if self.rSpeed < 160:
                self.rSpeed += 80 * globalClock.getDt()

            if mouse_dx != 0:
                self.camHeading -= self.rXSpeed * globalClock.getDt()
            else:
                self.camHeading -= self.rSpeed * globalClock.getDt()

            if self.camHeading < 0.0:
                self.camHeading = self.camHeading + 360.0
        else:
            self.rSpeed = 80

        if mouse_dy > 0:
            self.camPitch += self.rYSpeed * globalClock.getDt()
        elif mouse_dy < 0:
            self.camPitch -= self.rYSpeed * globalClock.getDt()

        # set camera heading and pitch
        base.camera.setHpr(self.camHeading, self.camPitch, 0)

        # viewer position (camera) movement control
        v = render.getRelativeVector(base.camera, Vec3.forward())
        if not self.flyMode:
            v.setZ(0.0)

        move_speed = self.cam_speeds[self.cam_speed]
        if self.keyMap["forward"] == 1:
            self.campos += v * move_speed * globalClock.getDt()
        if self.keyMap["backward"] == 1:
            self.campos -= v * move_speed * globalClock.getDt()

        # actually move the camera
        lastPos = base.camera.getPos()
        base.camera.setPos(self.campos)
        # self.plnp.setPos(self.campos)      # move the point light with the viewer position

        # WALKMODE: simple collision detection
        # we simply check a ray from slightly below the "eye point" straight down
        # for geometry collisions and if there are any we detect the point of collision
        # and adjust the camera's Z accordingly
        if self.flyMode == 0:
            # move the camera to where it would be if it made the move
            # the colliderNode moves with it
            # base.camera.setPos(self.campos)
            # check for collissons
            self.cTrav.traverse(render)
            entries = []
            for i in range(self.camGroundHandler.getNumEntries()):
                entry = self.camGroundHandler.getEntry(i)
                entries.append(entry)
                # print 'collision'
            entries.sort(lambda x, y: cmp(y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ()))

            if len(entries) > 0:  # and (entries[0].getIntoNode().getName() == "terrain"):
                # print len(entries)
                self.campos.setZ(entries[0].getSurfacePoint(render).getZ() + self.eyeHeight)
            else:
                self.campos = lastPos
                base.camera.setPos(self.campos)

            # if (base.camera.getZ() < self.player.getZ() + 2.0):
            #    base.camera.setZ(self.player.getZ() + 2.0)

        # update loc and hpr display
        pos = base.camera.getPos()
        hpr = base.camera.getHpr()
        self.inst2.setText("Loc: %.2f, %.2f, %.2f" % (pos.getX(), pos.getY(), pos.getZ()))
        self.inst3.setText("Hdg: %.2f, %.2f, %.2f" % (hpr.getX(), hpr.getY(), hpr.getZ()))
        return task.cont

    def exitGame(self):
        sys.exit(0)

    def resizeGame(self, win):
        props = base.win.getProperties()
        self.xres = props.getXSize()
        self.yres = props.getYSize()
        self.xres_half = self.xres / 2
        self.yres_half = self.yres / 2
        self.saveDefaultRes()

    # Records the state of the arrow keys
    # this is used for camera control
    def setKey(self, key, value):
        self.keyMap[key] = value

    # -------------------------------------------------------------------------
    # this is the mythical MAIN LOOP :)
    def update(self):

        if self.zone_reload_name != None:
            self.doReload(self.zone_reload_name)
            self.zone_reload_name = None

        if self.zone != None:
            self.zone.update()

        taskMgr.step()

        if self.login_client != None:
            self.login_client.update()

    # ZONE loading ------------------------------------------------------------

    # general zone loader driver
    # removes existing zone (if any) and load the new one
    def loadZone(self, name, path):
        if path[len(path) - 1] != "/":
            path += "/"

        if self.zone:
            self.zone.rootNode.removeNode()

        self.zone = Zone(self, name, path)
        error = self.zone.load()
        if error == 0:
            self.consoleOff()
            self.setFlymodeText()
            base.setBackgroundColor(self.fog_colour)

    def saveDefaultRes(self):
        cfg = self.configurator.config
        cfg["xres"] = str(self.xres)
        cfg["yres"] = str(self.yres)
        self.configurator.saveConfig()

    # initial world load after bootup
    def load(self):
        cfg = self.configurator.config

        if self.login_client != None:
            return

        zone_name = cfg["default_zone"]
        basepath = cfg["basepath"]
        self.loadZone(zone_name, basepath)

    # config save user interfacce
    def saveDefaultZone(self):
        if self.zone:
            cfg = self.configurator.config
            cfg["default_zone"] = self.zone.name
            self.configurator.saveConfig()

    # zone reload user interface

    # this gets called from our update loop when it detects that zone_reload_name has been set
    # we do this in this convoluted fashion in order to keep the main loop taskMgr updates ticking
    # because otherwise our status console output at various stages during the zone load would not
    # be displayed. Yes, this is hacky.
    def doReload(self, name):
        cfg = self.configurator.config
        basepath = cfg["basepath"]
        self.loadZone(name, basepath)

    # form dialog callback
    # this gets called from the form when the user has entered a something
    # (hopefully a correct zone short name)
    def reloadZoneDialogCB(self, name):
        self.frmDialog.end()
        self.zone_reload_name = name
        self.toggleControls(1)

    # this is called when the user presses "l"
    # it disables normal controls and fires up our query form dialog
    def reloadZone(self):
        base.setBackgroundColor((0, 0, 0))
        self.toggleControls(0)
        self.consoleOn()
        self.frmDialog = FileDialog(
            "Please enter the shortname of the zone you wish to load:",
            "Examples: qrg, blackburrow, freportn, crushbone etc.",
            self.reloadZoneDialogCB,
        )

        self.frmDialog.activate()  # relies on the main update loop to run

    ###############################
    # EXPERIMENTAL
    def doLogin(self):

        self.login_client = UDPClientStream("127.0.0.1", 5998)
Exemple #3
0
class World(DirectObject):

    def __init__(self):
        self.last_mousex = 0
        self.last_mousey = 0

        self.zone = None
        self.zone_reload_name = None
        
        self.winprops = WindowProperties( )

        # simple console output
        self.consoleNode = NodePath(PandaNode("console_root"))
        self.consoleNode.reparentTo(aspect2d)

        self.console_num_lines = 24
        self.console_cur_line = -1
        self.console_lines = []
        for i in range(0, self.console_num_lines):
            self.console_lines.append(OnscreenText(text='', style=1, fg=(1,1,1,1),
                        pos=(-1.3, .4-i*.05), align=TextNode.ALeft, scale = .035, parent = self.consoleNode))

        # Configuration
        self.consoleOut('zonewalk v.%s loading configuration' % VERSION)
        self.configurator = Configurator(self)
        cfg = self.configurator.config
        resaveRes = False
        if 'xres' in cfg:
            self.xres = int(cfg['xres'])
        else:
            self.xres = 1024
            resaveRes = True

        if 'yres' in cfg:
            self.yres = int(cfg['yres'])
        else:
            self.yres = 768
            resaveRes = True

        if resaveRes:
            self.saveDefaultRes()

        self.xres_half = self.xres / 2
        self.yres_half = self.yres / 2
        self.mouse_accum = MouseAccume( lambda: (self.xres_half,self.yres_half))

        self.eyeHeight = 7.0
        self.rSpeed = 80
        self.flyMode = 1

        # application window setup
        base.win.setClearColor(Vec4(0,0,0,1))
        self.winprops.setTitle( 'zonewalk')
        self.winprops.setSize(self.xres, self.yres) 
        
        base.win.requestProperties( self.winprops ) 
        base.disableMouse()

        # network test stuff
        self.login_client = None
        if 'testnet' in cfg:
            if cfg['testnet'] == '1':
                self.doLogin()
        
        # Post the instructions
        self.title = addTitle('zonewalk v.' + VERSION)
        self.inst0 = addInstructions(0.95, "[FLYMODE][1]")
        self.inst1 = addInstructions(-0.95, "Camera control with WSAD/mouselook. Press K for hotkey list, ESC to exit.")
        self.inst2 = addInstructions(0.9,  "Loc:")
        self.inst3 = addInstructions(0.85, "Hdg:")
        self.error_inst = addInstructions(0, '')
        self.kh = []
        
        self.campos = Point3(155.6, 41.2, 4.93)
        base.camera.setPos(self.campos)
        
        # Accept the application control keys: currently just esc to exit navgen       
        self.accept("escape", self.exitGame)
        self.accept("window-event", self.resizeGame)
        
        # Create some lighting
        ambient_level = .6
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(ambient_level, ambient_level, ambient_level, 1.0))
        render.setLight(render.attachNewNode(ambientLight))

        direct_level = 0.8
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(0.0, 0.0, -1.0))
        directionalLight.setColor(Vec4(direct_level, direct_level, direct_level, 1))
        directionalLight.setSpecularColor(Vec4(direct_level, direct_level, direct_level, 1))
        render.setLight(render.attachNewNode(directionalLight))
        
        # create a point light that will follow our view point (the camera for now)
        # attenuation is set so that this point light has a torch like effect
        self.plight = PointLight('plight')
        self.plight.setColor(VBase4(0.8, 0.8, 0.8, 1.0))
        self.plight.setAttenuation(Point3(0.0, 0.0, 0.0002))
        
        self.plnp = base.camera.attachNewNode(self.plight)
        self.plnp.setPos(0, 0, 0)
        render.setLight(self.plnp)
        self.cam_light = 1
        
        self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "cam-left":0, \
            "cam-right":0, "mouse3":0, "flymode":1 }

        # setup FOG
        self.fog_colour = (0.8,0.8,0.8,1.0)
        self.linfog = Fog("A linear-mode Fog node")
        self.linfog.setColor(self.fog_colour)
        self.linfog.setLinearRange(700, 980)         # onset, opaque distances as params
        # linfog.setLinearFallback(45,160,320)
        base.camera.attachNewNode(self.linfog)
        render.setFog(self.linfog)
        self.fog = 1
        
        # camera control
        self.campos = Point3(0, 0, 0)
        self.camHeading = 0.0
        self.camPitch = 0.0
        base.camLens.setFov(65.0)
        base.camLens.setFar(1200) 
        
        self.cam_speed = 0  # index into self.camp_speeds
        self.cam_speeds = [40.0, 80.0, 160.0, 320.0, 640.0]
        
        
        # Collision Detection for "WALKMODE"
        # We will detect the height of the terrain by creating a collision
        # ray and casting it downward toward the terrain.  The ray will start above the camera.
        # A ray may hit the terrain, or it may hit a rock or a tree.  If it
        # hits the terrain, we can detect the height.  If it hits anything
        # else, we rule that the move is illegal.
        
        self.cTrav = CollisionTraverser()
        self.camGroundRay = CollisionRay()
        self.camGroundRay.setOrigin(0.0, 0.0, 0.0)
        self.camGroundRay.setDirection(0,0,-1)      # straight down
        self.camGroundCol = CollisionNode('camRay')
        self.camGroundCol.addSolid(self.camGroundRay)
        self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
        
        # attach the col node to the camCollider dummy node
        self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)  
        self.camGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)
        
        
        # Uncomment this line to see the collision rays
        # self.camGroundColNp.show()
       
        # Uncomment this line to show a visual representation of the 
        # collisions occuring
        # self.cTrav.showCollisions(render)
        
        # Add the spinCameraTask procedure to the task manager.
        # taskMgr.add(self.spinCameraTask, "SpinCameraTask")
        taskMgr.add(self.camTask, "camTask")

        self.toggleControls(1)

        # need to step the task manager once to make our fake console work
        taskMgr.step()

    # CONSOLE ---------------------------------------------------------------------
    def consoleScroll(self):
        for i in range(0, self.console_num_lines-1):
            self.console_lines[i].setText(self.console_lines[i+1].getText())
            
    def consoleOut(self, text):
        print text  # output to stdout/log too

        if self.console_cur_line == self.console_num_lines-1:
            self.consoleScroll()
        elif self.console_cur_line < self.console_num_lines-1:
            self.console_cur_line += 1

        self.console_lines[self.console_cur_line].setText(text)

        taskMgr.step()
    
    def consoleOn(self):
        self.consoleNode.show()
        
    def consoleOff(self):
        self.consoleNode.hide()
        
    # User controls -----------------------------------------------------------
    def toggleControls(self, on):

        cfg = self.configurator.config

        if on == 1:
            self.accept("escape", self.exitGame)

            self.accept("1", self.setSpeed, ["speed", 0])
            self.accept("2", self.setSpeed, ["speed", 1])
            self.accept("3", self.setSpeed, ["speed", 2])
            self.accept("4", self.setSpeed, ["speed", 3])
            self.accept("5", self.setSpeed, ["speed", 4])

            self.accept("alt-f", self.fogToggle)

            self.accept(cfg['control_lighting'], self.camLightToggle)
            self.accept(cfg['control_help'], self.displayKeyHelp)
            self.accept(cfg['control_flymode'], self.toggleFlymode)
            self.accept(cfg['control_reload-zone'], self.reloadZone)
            # Deactivate this for now
            #self.accept("z", self.saveDefaultZone)
            self.accept(cfg['control_cam-left'], self.setKey, ["cam-left",1])
            self.accept(cfg['control_cam-right'], self.setKey, ["cam-right",1])
            self.accept(cfg['control_forward'], self.setKey, ["forward",1])
            # Mouse1 should be for clicking on objects
            #self.accept("mouse1", self.setKey, ["forward",1])
            self.accept("mouse3", self.setKey, ["mouse3",1])
            self.accept(cfg['control_backward'], self.setKey, ["backward",1])
        
            self.accept("k-up", self.hideKeyHelp)
            self.accept(cfg['control_cam-left']+"-up", self.setKey, ["cam-left",0])
            self.accept(cfg['control_cam-right']+"-up", self.setKey, ["cam-right",0])
            self.accept(cfg['control_forward']+"-up", self.setKey, ["forward",0])
            # Mouse1 should be for clicking on objects
            #self.accept("mouse1-up", self.setKey, ["forward",0])
            self.accept("mouse3-up", self.setKey, ["mouse3",0])
            self.accept(cfg['control_backward']+"-up", self.setKey, ["backward",0])
        else:
            messenger.clear()
            
    def setSpeed(self, key, value):
        self.cam_speed = value
        self.setFlymodeText()
        
    def fogToggle(self):
        if self.fog == 1:
            render.clearFog()
            base.camLens.setFar(100000) 
            self.fog = 0
        else:
            render.setFog(self.linfog)
            base.camLens.setFar(1200) 
            self.fog = 1
            
    def camLightToggle(self):
        if self.cam_light == 0:
            render.setLight(self.plnp)
            self.cam_light = 1
        else:
            render.clearLight(self.plnp)
            self.cam_light = 0
        
    def displayKeyHelp(self):
        self.kh = []
        msg = 'HOTKEYS:'
        pos = 0.75
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = '------------------'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'W: camera fwd, S: camera bck, A: rotate view left, D: rotate view right'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = '1-5: set camera movement speed'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'F: toggle Flymode/Walkmode'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'L: load a zone'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'ALT-F: toggle FOG and FAR plane on/off'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'T: toggle additional camera "torch" light on/off'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'Z: set currently loaded zone as new startup default'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'ESC: exit zonewalk'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
     
    def hideKeyHelp(self):
        for n in self.kh:
            n.removeNode()
                        
    def setFlymodeText(self):
        zname = ''
        if self.zone:
            zname = self.zone.name
            
        if self.flyMode == 0:
            self.inst0.setText("[WALKMODE][%i] %s" % (self.cam_speed+1, zname))
        else:
            self.inst0.setText("[FLYMODE][%i] %s " % (self.cam_speed+1, zname))
        
    def toggleFlymode(self):
        zname = ''
        if self.zone:
            zname = self.zone.name

        if self.flyMode == 0:
            self.flyMode = 1
        else:
            self.flyMode = 0
            
        self.setFlymodeText()

    # Define a procedure to move the camera.
    def spinCameraTask(self, task):
        angleDegrees = task.time * 6.0
        angleRadians = angleDegrees * (pi / 180.0)
        base.camera.setPos(20 * sin(angleRadians), -20.0 * cos(angleRadians), 3)
        base.camera.setHpr(angleDegrees, 0, 0)
        return task.cont


    def camTask(self, task):
        # query the mouse
        mouse_dx = 0
        mouse_dy = 0


        # if we have a mouse and the right button is depressed
        if base.mouseWatcherNode.hasMouse():
            if self.keyMap["mouse3"] != 0:
                self.mouse_accum.update()
            else:
                self.mouse_accum.reset()

        mouse_dx = self.mouse_accum.dx
        mouse_dy = self.mouse_accum.dy

        self.rXSpeed = fabs(self.mouse_accum.dx) * (self.cam_speed+1) * max(5 * 1000/self.xres,3)
        self.rYSpeed = fabs(self.mouse_accum.dy) * (self.cam_speed+1) * max(3 * 1000/self.yres,1)
            
        if (self.keyMap["cam-left"]!=0 or mouse_dx < 0):
            if self.rSpeed < 160:
                self.rSpeed += 80 * globalClock.getDt()

            if mouse_dx != 0:
                self.camHeading += self.rXSpeed * globalClock.getDt()
            else:
                self.camHeading += self.rSpeed * globalClock.getDt()

            if self.camHeading > 360.0:
                self.camHeading = self.camHeading - 360.0
        elif (self.keyMap["cam-right"]!=0 or mouse_dx > 0):
            if self.rSpeed < 160:
                self.rSpeed += 80 * globalClock.getDt()

            if mouse_dx != 0:
                self.camHeading -= self.rXSpeed * globalClock.getDt()
            else:
                self.camHeading -= self.rSpeed * globalClock.getDt()

            if self.camHeading < 0.0:
                self.camHeading = self.camHeading + 360.0
        else:
            self.rSpeed = 80

        if mouse_dy > 0:
            self.camPitch += self.rYSpeed * globalClock.getDt()
        elif mouse_dy < 0:
            self.camPitch -= self.rYSpeed * globalClock.getDt()
            
        # set camera heading and pitch
        base.camera.setHpr(self.camHeading, self.camPitch, 0)

        # viewer position (camera) movement control
        v = render.getRelativeVector(base.camera, Vec3.forward())
        if not self.flyMode:
            v.setZ(0.0)
        
        move_speed = self.cam_speeds[self.cam_speed]
        if self.keyMap["forward"] == 1:
            self.campos += v * move_speed * globalClock.getDt()
        if self.keyMap["backward"] == 1:
            self.campos -= v * move_speed * globalClock.getDt()            

        # actually move the camera
        lastPos = base.camera.getPos()
        base.camera.setPos(self.campos)
        # self.plnp.setPos(self.campos)      # move the point light with the viewer position

        # WALKMODE: simple collision detection
        # we simply check a ray from slightly below the "eye point" straight down
        # for geometry collisions and if there are any we detect the point of collision
        # and adjust the camera's Z accordingly
        if self.flyMode == 0:   
            # move the camera to where it would be if it made the move 
            # the colliderNode moves with it
            # base.camera.setPos(self.campos)
            # check for collissons
            self.cTrav.traverse(render)
            entries = []
            for i in range(self.camGroundHandler.getNumEntries()):
                entry = self.camGroundHandler.getEntry(i)
                entries.append(entry)
                # print 'collision'
            entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                         x.getSurfacePoint(render).getZ()))
                                     
            if (len(entries) > 0): # and (entries[0].getIntoNode().getName() == "terrain"):
                # print len(entries)
                self.campos.setZ(entries[0].getSurfacePoint(render).getZ()+self.eyeHeight)
            else:
                self.campos = lastPos
                base.camera.setPos(self.campos)
        
            #if (base.camera.getZ() < self.player.getZ() + 2.0):
            #    base.camera.setZ(self.player.getZ() + 2.0)


        # update loc and hpr display
        pos = base.camera.getPos()
        hpr = base.camera.getHpr()
        self.inst2.setText('Loc: %.2f, %.2f, %.2f' % (pos.getX(), pos.getY(), pos.getZ()))
        self.inst3.setText('Hdg: %.2f, %.2f, %.2f' % (hpr.getX(), hpr.getY(), hpr.getZ()))
        return task.cont

        
    def exitGame(self):           
        sys.exit(0)

    def resizeGame(self,win):
        props = base.win.getProperties() 
        self.xres = props.getXSize()
        self.yres = props.getYSize()
        self.xres_half = self.xres / 2
        self.yres_half = self.yres / 2
        self.saveDefaultRes()
                
    #Records the state of the arrow keys
    # this is used for camera control
    def setKey(self, key, value):
        self.keyMap[key] = value

    # -------------------------------------------------------------------------
    # this is the mythical MAIN LOOP :)
    def update(self):

        if self.zone_reload_name != None:
            self.doReload(self.zone_reload_name)
            self.zone_reload_name = None

        if self.zone != None:
            self.zone.update()

        taskMgr.step()
   
        if self.login_client != None:
            self.login_client.update()  
        
        
    # ZONE loading ------------------------------------------------------------
    
    # general zone loader driver
    # removes existing zone (if any) and load the new one 
    def loadZone(self, name, path):
        if path[len(path)-1] != '/':
            path += '/'

        if self.zone:
            self.zone.rootNode.removeNode()
            
        self.zone = Zone(self, name, path)
        error = self.zone.load()
        if error == 0:
            self.consoleOff()
            self.setFlymodeText()
            base.setBackgroundColor(self.fog_colour)
        
    def saveDefaultRes(self):
        cfg = self.configurator.config
        cfg['xres'] = str(self.xres)
        cfg['yres'] = str(self.yres)
        #self.configurator.saveConfig()

    # initial world load after bootup
    def load(self):       
        cfg = self.configurator.config
        
        if self.login_client != None:
            return
            
        zone_name = cfg['default_zone']
        basepath = cfg['basepath']
        self.loadZone(zone_name, basepath)
    

    # config save user interfacce
    def saveDefaultZone(self):
        if self.zone:
            cfg = self.configurator.config
            cfg['default_zone'] = self.zone.name
            #self.configurator.saveConfig()

    # zone reload user interface
    
    # this gets called from our update loop when it detects that zone_reload_name has been set
    # we do this in this convoluted fashion in order to keep the main loop taskMgr updates ticking
    # because otherwise our status console output at various stages during the zone load would not
    # be displayed. Yes, this is hacky.
    def doReload(self, name):
        cfg = self.configurator.config
        basepath = cfg['basepath']
        self.loadZone(name, basepath)

    # form dialog callback
    # this gets called from the form when the user has entered a something
    # (hopefully a correct zone short name)
    def reloadZoneDialogCB(self, name):
        self.frmDialog.end()
        self.zone_reload_name = name
        self.toggleControls(1)

    # this is called when the user presses "l"
    # it disables normal controls and fires up our query form dialog
    def reloadZone(self):
        base.setBackgroundColor((0,0,0))
        self.toggleControls(0)
        self.consoleOn()
        self.frmDialog = FileDialog(
            "Please enter the shortname of the zone you wish to load:", 
            "Examples: qrg, blackburrow, freportn, crushbone etc.",
            self.reloadZoneDialogCB) 
        
        self.frmDialog.activate()   # relies on the main update loop to run

    ###############################
    # EXPERIMENTAL         
    def doLogin(self):
        
        self.login_client = UDPClientStream('127.0.0.1', 5998)

    #####################################
    # Custom methods
    #####################################


    # What happens when a user clicks on a model
    def onModelClick():
        # Code adapted freely from http://www.panda3d.org/forums/viewtopic.php?t=12717
        global picker, selected_model
        namedNode, thePoint, rawNode = picker.pick()
        if namedNode:
            if "_mesh" not in namedNode.getName():  # rough test to avoid printing infos on global zone mesh (ie: "freporte_mesh")
                name = namedNode.getName()
                p = namedNode.getParent()
                pos = p.getPos()
                selected_model = namedNode
                print namedNode.getName()
                print "Collision Point: ", thePoint

                namedNode.ls()
            else:
                print "Clicked location point (y, x, z):", thePoint
                #selected_model.setPos(thePoint.getX(), thePoint.getY(), thePoint.getZ())

                m.setPos(thePoint.getX(), thePoint.getY(), thePoint.getZ())
                print "Moved !"

    # Handles populating the zone with spawn data from the EQEmu DB
    # also makes each spawner model pickable
    def PopulateSpawns(self, cursor, numrows):
        spawn_coords = list()
        globals.model_list = list()
        for x in range(0, numrows):
            row = cursor.fetchone()
            point = Point3(long(row["Spawn2Y"]), long(row["Spawn2X"]), long(row["Spawn2Z"]))
            if point not in spawn_coords:
                s = loader.loadModel("models/cube.egg")
                s.reparentTo(render)
                s.setPos(row["Spawn2Y"], row["Spawn2X"], row["Spawn2Z"])
                min,macks= s.getTightBounds()
                radius = max([macks.getY() - min.getY(), macks.getX() - min.getX()])/2
                cs = CollisionSphere(row["Spawn2X"], row["Spawn2Y"], row["Spawn2Z"], radius)
                csNode = s.attachNewNode(CollisionNode("modelCollide"))
                csNode.node().addSolid(cs)
                s.setTag("name", row["name"])
                picker.makePickable(s)
                globals.model_list.append(s)
                spawn_coords.append(point)

    # Establishes a connection to the EQEmu database
    def ConnectToDatabase(self):
        configurator = Configurator(world)
        cfg = configurator.config
        conn = MySQLdb.Connection(
            host=cfg['host'],
            user=cfg['user'],
            passwd=cfg['password'],
            db=cfg['db'])

        return conn

    # Queries the Database in order to get spawn data
    # (this should be refactored at some point)
    def GetDbSpawnData(self, connection):
        cursor = connection.cursor(MySQLdb.cursors.DictCursor)

        query = """SELECT nt.name, s2.zone, s2.x as Spawn2X, s2.y as Spawn2Y, s2.z as Spawn2Z, sg.name as spawngroup_name,sg.id as Spawngroup_id, sg.min_x as Spawngroup_minX, sg.max_x as Spawngroup_maxX, sg.min_y as Spawngroup_minY, sg.max_y as Spawngroup_maxY, sg.dist as Spawngroup_dist, sg.mindelay as Spawngroup_mindelay,
                sg.delay as Spawngroup_delay FROM spawn2 s2
                JOIN spawngroup sg ON sg.id = s2.spawngroupid
                JOIN spawnentry se
                ON se.spawngroupid = sg.id
                JOIN npc_types nt
                ON nt.id = se.npcid
                WHERE s2.zone = 'freporte'"""
        cursor.execute(query)
        return cursor

    # Initializes the camera position upon startup
    def InitCameraPosition(self):
        world.campos = Point3(-155.6, 41.2, 4.9 + world.eyeHeight)
        world.camHeading = 270.0

        base.camera.setPos(world.campos)

    def GetCamera(self):
        return base.camera
Exemple #4
0
class World(DirectObject):
    cfg = None
    def __init__(self):
        self.last_mousex = 0
        self.last_mousey = 0

        self.zone = None
        self.zone_reload_name = None
        
        self.winprops = WindowProperties( )

        # simple console output
        self.consoleNode = NodePath(PandaNode("console_root"))
        self.consoleNode.reparentTo(aspect2d)

        self.console_num_lines = 24
        self.console_cur_line = -1
        self.console_lines = []
        for i in range(0, self.console_num_lines):
            self.console_lines.append(OnscreenText(text='', style=1, fg=(1,1,1,1),
                        pos=(-1.3, .4-i*.05), align=TextNode.ALeft, scale = .035, parent = self.consoleNode))

        # Configuration
        self.consoleOut('World Forge v.%s loading configuration' % VERSION)
        self.configurator = Configurator(self)
        cfg = self.configurator.config
        resaveRes = False
        if 'xres' in cfg:
            self.xres = int(cfg['xres'])
        else:
            self.xres = 1024
            resaveRes = True

        if 'yres' in cfg:
            self.yres = int(cfg['yres'])
        else:
            self.yres = 768
            resaveRes = True

        if resaveRes:
            self.saveDefaultRes()

        self.xres_half = self.xres / 2
        self.yres_half = self.yres / 2
        self.mouse_accum = MouseAccume( lambda: (self.xres_half,self.yres_half))

        self.eyeHeight = 7.0
        self.rSpeed = 80
        self.flyMode = 1

        # application window setup
        base.win.setClearColor(Vec4(0,0,0,1))
        self.winprops.setTitle( 'World Forge')
        self.winprops.setSize(self.xres, self.yres) 
        
        base.win.requestProperties( self.winprops ) 
        base.disableMouse()
        
        # Post the instructions
        self.title = addTitle('World Forge v.' + VERSION)
        self.inst0 = addInstructions(0.95, "[FLYMODE][1]")
        self.inst1 = addInstructions(-0.95, "Camera control with WSAD/mouselook. Press K for hotkey list, ESC to exit.")
        self.inst2 = addInstructions(0.9,  "Loc:")
        self.inst3 = addInstructions(0.85, "Hdg:")
        self.error_inst = addInstructions(0, '')
        self.kh = []
        
        self.campos = Point3(155.6, 41.2, 4.93)
        base.camera.setPos(self.campos)
        
        # Accept the application control keys: currently just esc to exit navgen       
        self.accept("escape", self.exitGame)
        self.accept("window-event", self.resizeGame)
        
        # Create some lighting
        ambient_level = .6
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(ambient_level, ambient_level, ambient_level, 1.0))
        render.setLight(render.attachNewNode(ambientLight))

        direct_level = 0.8
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(0.0, 0.0, -1.0))
        directionalLight.setColor(Vec4(direct_level, direct_level, direct_level, 1))
        directionalLight.setSpecularColor(Vec4(direct_level, direct_level, direct_level, 1))
        render.setLight(render.attachNewNode(directionalLight))
        
        # create a point light that will follow our view point (the camera for now)
        # attenuation is set so that this point light has a torch like effect
        self.plight = PointLight('plight')
        self.plight.setColor(VBase4(0.8, 0.8, 0.8, 1.0))
        self.plight.setAttenuation(Point3(0.0, 0.0, 0.0002))
        
        self.plnp = base.camera.attachNewNode(self.plight)
        self.plnp.setPos(0, 0, 0)
        render.setLight(self.plnp)
        self.cam_light = 1
        
        self.keyMap = {"left":0, "right":0, "forward":0, "backward":0, "cam-left":0, \
            "cam-right":0, "mouse3":0, "flymode":1 }

        # setup FOG
        self.fog_colour = (0.8,0.8,0.8,1.0)
        self.linfog = Fog("A linear-mode Fog node")
        self.linfog.setColor(self.fog_colour)
        self.linfog.setLinearRange(700, 980)         # onset, opaque distances as params
        # linfog.setLinearFallback(45,160,320)
        base.camera.attachNewNode(self.linfog)
        render.setFog(self.linfog)
        self.fog = 1
        
        # camera control
        self.campos = Point3(0, 0, 0)
        self.camHeading = 0.0
        self.camPitch = 0.0
        base.camLens.setFov(65.0)
        base.camLens.setFar(1200) 
        
        self.cam_speed = 0  # index into self.camp_speeds
        self.cam_speeds = [40.0, 80.0, 160.0, 320.0, 640.0]
        
        
        # Collision Detection for "WALKMODE"
        # We will detect the height of the terrain by creating a collision
        # ray and casting it downward toward the terrain.  The ray will start above the camera.
        # A ray may hit the terrain, or it may hit a rock or a tree.  If it
        # hits the terrain, we can detect the height.  If it hits anything
        # else, we rule that the move is illegal.
        
        self.cTrav = CollisionTraverser()
        self.camGroundRay = CollisionRay()
        self.camGroundRay.setOrigin(0.0, 0.0, 0.0)
        self.camGroundRay.setDirection(0,0,-1)      # straight down
        self.camGroundCol = CollisionNode('camRay')
        self.camGroundCol.addSolid(self.camGroundRay)
        self.camGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.camGroundCol.setIntoCollideMask(BitMask32.allOff())
        
        # attach the col node to the camCollider dummy node
        self.camGroundColNp = base.camera.attachNewNode(self.camGroundCol)  
        self.camGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)
        
        
        # Uncomment this line to see the collision rays
        # self.camGroundColNp.show()
       
        # Uncomment this line to show a visual representation of the 
        # collisions occuring
        # self.cTrav.showCollisions(render)
        
        # Add the spinCameraTask procedure to the task manager.
        # taskMgr.add(self.spinCameraTask, "SpinCameraTask")
        globals.hasClickedSpawn = False;
        globals.hasClickedGrid = False;
        taskMgr.add(self.camTask, "camTask")

        self.toggleControls(1)

        # need to step the task manager once to make our fake console work
        taskMgr.step()

    # CONSOLE ---------------------------------------------------------------------
    def consoleScroll(self):
        for i in range(0, self.console_num_lines-1):
            self.console_lines[i].setText(self.console_lines[i+1].getText())
            
    def consoleOut(self, text):
        print text  # output to stdout/log too

        if self.console_cur_line == self.console_num_lines-1:
            self.consoleScroll()
        elif self.console_cur_line < self.console_num_lines-1:
            self.console_cur_line += 1

        self.console_lines[self.console_cur_line].setText(text)

        taskMgr.step()
    
    def consoleOn(self):
        self.consoleNode.show()
        
    def consoleOff(self):
        self.consoleNode.hide()
        
    # User controls -----------------------------------------------------------
    def toggleControls(self, on):

        cfg = self.configurator.config

        if on == 1:
            self.accept("escape", self.exitGame)

            self.accept("1", self.setSpeed, ["speed", 0])
            self.accept("2", self.setSpeed, ["speed", 1])
            self.accept("3", self.setSpeed, ["speed", 2])
            self.accept("4", self.setSpeed, ["speed", 3])
            self.accept("5", self.setSpeed, ["speed", 4])

            self.accept("alt-f", self.fogToggle)

            self.accept(cfg['control_lighting'], self.camLightToggle)
            self.accept(cfg['control_help'], self.displayKeyHelp)
            self.accept(cfg['control_flymode'], self.toggleFlymode)
            self.accept(cfg['control_reload-zone'], self.reloadZone)
            self.accept(cfg['control_cam-left'], self.setKey, ["cam-left",1])
            self.accept(cfg['control_cam-right'], self.setKey, ["cam-right",1])
            self.accept(cfg['control_forward'], self.setKey, ["forward",1])
            self.accept("mouse3", self.setKey, ["mouse3",1])
            self.accept(cfg['control_backward'], self.setKey, ["backward",1])
        
            self.accept("k-up", self.hideKeyHelp)
            self.accept(cfg['control_cam-left']+"-up", self.setKey, ["cam-left",0])
            self.accept(cfg['control_cam-right']+"-up", self.setKey, ["cam-right",0])
            self.accept(cfg['control_forward']+"-up", self.setKey, ["forward",0])
            self.accept("mouse3-up", self.setKey, ["mouse3",0])
            self.accept(cfg['control_backward']+"-up", self.setKey, ["backward",0])
            self.accept(cfg['toggle_edit-mode'], self.toggleEditMode)
            self.accept(cfg['toggle_insert-mode'], self.toggleInsertMode)
            self.accept(cfg['toggle_explore-mode'], self.toggleExploreMode)
            self.accept(cfg['toggle_grid-mode'], self.toggleGridMode)
            # Accept both single-presses and long presses for rotating models
            self.accept(cfg['rotate-right'] + "-repeat", self.rotateModelRight)
            self.accept(cfg['rotate-left'] + "-repeat", self.rotateModelLeft)
            self.accept(cfg['rotate-right'], self.rotateModelRight)
            self.accept(cfg['rotate-left'], self.rotateModelLeft)
            self.accept(cfg['clear-selection'], self.clearSelection)
        else:
            messenger.clear()

    def rotateModelRight(self):
        if globals.editMode == True:
            if globals.selectedSpawn:
                cfg = self.configurator.config
                globals.selectedSpawn.model.setH(globals.selectedSpawn.model.getH() + int(cfg['rotation-amount']))
                # Really not sure about that...
                if globals.selectedSpawn.model.getH() > 360:
                    globals.selectedSpawn.model.setH(0)
                print globals.selectedSpawn.model.getH()
                globals.selectedSpawn.setheadingfromworld(globals.selectedSpawn.model.getH())
                globals.spawndialog.m_spawnEntryHeadingTextCtrl.SetValue(str(globals.selectedSpawn.spawnentry_heading))
                if globals.config['autosave_edit-mode'] == 'True':
                    globals.database.UpdateSpawn(globals.selectedSpawn)
                print globals.selectedSpawn.spawnentry_heading

    def rotateModelLeft(self):
        if globals.editMode == True:
            if globals.selectedSpawn:
                cfg = self.configurator.config
                globals.selectedSpawn.model.setH(globals.selectedSpawn.model.getH() - int(cfg['rotation-amount']))
                # Really not sure about that either...
                if globals.selectedSpawn.model.getH() < -360:
                    globals.selectedSpawn.model.setH(0)
                print globals.selectedSpawn.model.getH()
                globals.selectedSpawn.setheadingfromworld(globals.selectedSpawn.model.getH())
                globals.spawndialog.m_spawnEntryHeadingTextCtrl.SetValue(str(globals.selectedSpawn.spawnentry_heading))
                if globals.config['autosave_edit-mode'] == 'True':
                    globals.database.UpdateSpawn(globals.selectedSpawn)
                print globals.selectedSpawn.spawnentry_heading

    def clearSelection(self, eraseNpcId = True):
        globals.selectedspawn = None
        globals.selectedgrid = None
        globals.picker.lastSelectedObject = None
        if self.inst6:
            self.inst6.destroy()
        self.inst6 = addInstructions(0.7, "Current selection: None")
        npcid = globals.spawndialog.m_spawnEntryNpcIdTextCtrl.Value
        globals.spawndialog.Reset()
        # f*****g hacky shit man
        if eraseNpcId == False:
            globals.spawndialog.m_spawnEntryNpcIdTextCtrl.SetValue(npcid)
        gridmanager = GridpointManager()
        gridmanager.ResetGridList()
        print "Cleared all selections !"

    def toggleDefaultMode(self):
        globals.editMode = False
        globals.insertMode = False
        globals.exploreMode = True
        globals.gridMode = False
        print "STARTUP Explore mode ACTIVATED"
        print "STARTUP Grid mode DEACTIVATED"
        self.inst4 = addInstructions(0.8, "Explore mode ON")
        self.inst5 = addInstructions(0.75, "Grid mode OFF")
        self.inst6 = addInstructions(0.7, "Current selection: None")

    def toggleEditMode(self):
        globals.editMode = True
        globals.insertMode = False
        globals.exploreMode = False
        print "Edit mode ACTIVATED"
        if self.inst4:
            self.inst4.destroy()
        self.inst4 = addInstructions(0.8, "Edit mode ON")

    def toggleInsertMode(self):
        globals.editMode = False
        globals.insertMode = True
        globals.exploreMode = False
        print "Insert mode ACTIVATED"
        if self.inst4:
            self.inst4.destroy()
        self.inst4 = addInstructions(0.8, "Insert mode ON")

    def toggleExploreMode(self):
        globals.editMode = False
        globals.insertMode = False
        globals.exploreMode = True
        print "Explore mode ACTIVATED"
        if self.inst4:
            self.inst4.destroy()
        self.inst4 = addInstructions(0.8, "Explore mode ON")

    def toggleGridMode(self):
        if globals.gridMode == False:
            globals.gridMode = True
            print "Grid mode ACTIVATED"
            if self.inst5:
                self.inst5.destroy()
            self.inst5 = addInstructions(0.75, "Grid mode ON")
        else:
            globals.gridMode = False
            print "Grid mode DEACTIVATED"
            if self.inst5:
                self.inst5.destroy()
            self.inst5 = addInstructions(0.75, "Grid mode OFF")

    def setSpeed(self, key, value):
        self.cam_speed = value
        self.setFlymodeText()
        
    def fogToggle(self):
        if self.fog == 1:
            render.clearFog()
            base.camLens.setFar(100000) 
            self.fog = 0
        else:
            render.setFog(self.linfog)
            base.camLens.setFar(1200) 
            self.fog = 1
            
    def camLightToggle(self):
        if self.cam_light == 0:
            render.setLight(self.plnp)
            self.cam_light = 1
        else:
            render.clearLight(self.plnp)
            self.cam_light = 0
        
    def displayKeyHelp(self):
        self.kh = []
        msg = 'HOTKEYS:'
        pos = 0.75
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = '------------------'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'W: camera fwd, S: camera bck, A: rotate view left, D: rotate view right'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = '1-5: set camera movement speed'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'F: toggle Flymode/Walkmode'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'L: load a zone'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'ALT-F: toggle FOG and FAR plane on/off'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'T: toggle additional camera "torch" light on/off'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'Z: set currently loaded zone as new startup default'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
        msg = 'ESC: exit World Forge'
        pos -= 0.05
        self.kh.append(OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-0.5, pos), align=TextNode.ALeft, scale = .04))
     
    def hideKeyHelp(self):
        for n in self.kh:
            n.removeNode()
                        
    def setFlymodeText(self):
        zname = ''
        if self.zone:
            zname = self.zone.name
            
        if self.flyMode == 0:
            self.inst0.setText("[WALKMODE][%i] %s" % (self.cam_speed+1, zname))
        else:
            self.inst0.setText("[FLYMODE][%i] %s " % (self.cam_speed+1, zname))
        
    def toggleFlymode(self):
        zname = ''
        if self.zone:
            zname = self.zone.name

        if self.flyMode == 0:
            self.flyMode = 1
        else:
            self.flyMode = 0
            
        self.setFlymodeText()

    # Define a procedure to move the camera.
    def spinCameraTask(self, task):
        angleDegrees = task.time * 6.0
        angleRadians = angleDegrees * (pi / 180.0)
        base.camera.setPos(20 * sin(angleRadians), -20.0 * cos(angleRadians), 3)
        base.camera.setHpr(angleDegrees, 0, 0)
        return task.cont


    def camTask(self, task):
        if globals.hasClickedSpawn:
           base.camera.setPos(globals.selectedSpawnPoint3D)
           self.campos = globals.selectedSpawnPoint3D
           globals.hasClickedSpawn = False
        elif globals.hasClickedGrid:
            base.camera.setPos(globals.selectedGridPoint3D)
            self.campos = globals.selectedGridPoint3D
            globals.hasClickedGrid = False
        else:
            # query the mouse
            mouse_dx = 0
            mouse_dy = 0


            # if we have a mouse and the right button is depressed
            if base.mouseWatcherNode.hasMouse():
                if self.keyMap["mouse3"] != 0:
                    self.mouse_accum.update()
                else:
                    self.mouse_accum.reset()

            mouse_dx = self.mouse_accum.dx
            mouse_dy = self.mouse_accum.dy

            self.rXSpeed = fabs(self.mouse_accum.dx) * (self.cam_speed+1) * max(5 * 1000/self.xres,3)
            self.rYSpeed = fabs(self.mouse_accum.dy) * (self.cam_speed+1) * max(3 * 1000/self.yres,1)
            
            if (self.keyMap["cam-left"]!=0 or mouse_dx < 0):
                if self.rSpeed < 160:
                    self.rSpeed += 80 * globalClock.getDt()

                if mouse_dx != 0:
                    self.camHeading += self.rXSpeed * globalClock.getDt()
                else:
                    self.camHeading += self.rSpeed * globalClock.getDt()

                if self.camHeading > 360.0:
                    self.camHeading = self.camHeading - 360.0
            elif (self.keyMap["cam-right"]!=0 or mouse_dx > 0):
                if self.rSpeed < 160:
                    self.rSpeed += 80 * globalClock.getDt()

                if mouse_dx != 0:
                    self.camHeading -= self.rXSpeed * globalClock.getDt()
                else:
                    self.camHeading -= self.rSpeed * globalClock.getDt()

                if self.camHeading < 0.0:
                    self.camHeading = self.camHeading + 360.0
            else:
                self.rSpeed = 80

            if mouse_dy > 0:
                self.camPitch += self.rYSpeed * globalClock.getDt()
            elif mouse_dy < 0:
                self.camPitch -= self.rYSpeed * globalClock.getDt()
            
            # set camera heading and pitch
            base.camera.setHpr(self.camHeading, self.camPitch, 0)

            # viewer position (camera) movement control
            v = render.getRelativeVector(base.camera, Vec3.forward())
            if not self.flyMode:
                v.setZ(0.0)
        
            move_speed = self.cam_speeds[self.cam_speed]
            if self.keyMap["forward"] == 1:
                self.campos += v * move_speed * globalClock.getDt()
            if self.keyMap["backward"] == 1:
                self.campos -= v * move_speed * globalClock.getDt()            

            # actually move the camera
            lastPos = base.camera.getPos()
            base.camera.setPos(self.campos)
            # self.plnp.setPos(self.campos)      # move the point light with the viewer position

            # WALKMODE: simple collision detection
            # we simply check a ray from slightly below the "eye point" straight down
            # for geometry collisions and if there are any we detect the point of collision
            # and adjust the camera's Z accordingly
            if self.flyMode == 0:   
                # move the camera to where it would be if it made the move 
                # the colliderNode moves with it
                # base.camera.setPos(self.campos)
                # check for collissons
                self.cTrav.traverse(render)
                entries = []
                for i in range(self.camGroundHandler.getNumEntries()):
                    entry = self.camGroundHandler.getEntry(i)
                    entries.append(entry)
                    # print 'collision'
                entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                             x.getSurfacePoint(render).getZ()))
                                     
                if (len(entries) > 0): # and (entries[0].getIntoNode().getName() == "terrain"):
                    # print len(entries)
                    self.campos.setZ(entries[0].getSurfacePoint(render).getZ()+self.eyeHeight)
                else:
                    self.campos = lastPos
                    base.camera.setPos(self.campos)
        
                #if (base.camera.getZ() < self.player.getZ() + 2.0):
                #    base.camera.setZ(self.player.getZ() + 2.0)


            # update loc and hpr display
            pos = base.camera.getPos()
            hpr = base.camera.getHpr()
            self.inst2.setText('Loc: %.2f, %.2f, %.2f' % (pos.getX(), pos.getY(), pos.getZ()))
            self.inst3.setText('Hdg: %.2f, %.2f, %.2f' % (hpr.getX(), hpr.getY(), hpr.getZ()))
        return task.cont

    def exitGame(self):
        globals.database.conn.close()
        print "DB connection closed !"
        sys.exit(0)

    def resizeGame(self,win):
        props = base.win.getProperties() 
        self.xres = props.getXSize()
        self.yres = props.getYSize()
        self.xres_half = self.xres / 2
        self.yres_half = self.yres / 2
        self.saveDefaultRes()
                
    #Records the state of the arrow keys
    # this is used for camera control
    def setKey(self, key, value):
        self.keyMap[key] = value

    # -------------------------------------------------------------------------
    # this is the mythical MAIN LOOP :)
    def update(self):

        if self.zone_reload_name != None:
            self.doReload(self.zone_reload_name)
            self.zone_reload_name = None

        if self.zone != None:
            self.zone.update()

        taskMgr.step()
        
        
    # ZONE loading ------------------------------------------------------------
    
    # general zone loader driver
    # removes existing zone (if any) and load the new one 
    def loadZone(self, name, path):
        if path[len(path)-1] != '/':
            path += '/'

        if self.zone:
            self.zone.rootNode.removeNode()
            
        self.zone = Zone(self, name, path)
        error = self.zone.load()
        if error == 0:
            self.consoleOff()
            self.setFlymodeText()
            base.setBackgroundColor(self.fog_colour)
        
    def saveDefaultRes(self):
        cfg = self.configurator.config
        cfg['xres'] = str(self.xres)
        cfg['yres'] = str(self.yres)

    # initial world load after bootup
    def load(self):       
        cfg = self.configurator.config
            
        zone_name = cfg['default_zone']
        globals.currentZone = zone_name
        basepath = cfg['basepath']
        self.loadZone(zone_name, basepath)

    # zone reload user interface
    
    # this gets called from our update loop when it detects that zone_reload_name has been set
    # we do this in this convoluted fashion in order to keep the main loop taskMgr updates ticking
    # because otherwise our status console output at various stages during the zone load would not
    # be displayed. Yes, this is hacky.
    def doReload(self, name):
        cfg = self.configurator.config
        basepath = cfg['basepath']
        self.loadZone(name, basepath)

    # form dialog callback
    # this gets called from the form when the user has entered a something
    # (hopefully a correct zone short name)
    def reloadZoneDialogCB(self, name):
        self.frmDialog.end()
        self.zone_reload_name = name
        self.toggleControls(1)

    # this is called when the user presses "l"
    # it disables normal controls and fires up our query form dialog
    def reloadZone(self):
        base.setBackgroundColor((0,0,0))
        self.toggleControls(0)
        self.consoleOn()
        self.frmDialog = FileDialog(
            "Please enter the shortname of the zone you wish to load:", 
            "Examples: qrg, blackburrow, freportn, crushbone etc.",
            self.reloadZoneDialogCB) 
        
        self.frmDialog.activate()   # relies on the main update loop to run

    #####################################
    # Custom methods
    #####################################


    # Handles populating the zone with spawn data from the EQEmu DB
    # also makes each spawner model pickable
    def PopulateSpawns(self, cursor, numrows):
        spawn_coords = list()
        globals.spawn_list = list()
        cfg = self.configurator.config
        for x in range(0, numrows):
            row = cursor.fetchone()
            point = Point3(long(row["Spawn2Y"]), long(row["Spawn2X"]), long(row["Spawn2Z"]))
            if cfg['ignore_duplicate_spawns'] == 'True':
                if point not in spawn_coords:
                    self.PlaceSpawnPointOn3dMap(row)
                    spawn_coords.append(point)
            else:
                self.PlaceSpawnPointOn3dMap(row)

    def PlaceSpawnPointOn3dMap(self, row):
        spawn = Spawn()
        self.InitSpawnData(spawn, row)
        spawn.model = loader.loadModel(spawn.modelname)
        spawn.initmodel()
        spawn.model.reparentTo(render)
        spawn.initheadingfromdb(row["Spawn2Heading"])
        spawn.placeintoworld(row["Spawn2Y"], row["Spawn2X"], row["Spawn2Z"])
        min, macks = spawn.model.getTightBounds()
        radius = max([macks.getY() - min.getY(), macks.getX() - min.getX()]) / 2
        cs = CollisionSphere(row["Spawn2X"], row["Spawn2Y"], row["Spawn2Z"], radius)
        csNode = spawn.model.attachNewNode(CollisionNode("modelCollide"))
        csNode.node().addSolid(cs)
        # TODO: ADD MORE TAGS??
        spawn.model.setTag("name", row["NpcName"])
        spawn.model.setTag("spawngroup_name", row["spawngroup_name"])
        spawn.model.setTag("spawn2id", str(row["Spawn2Id"]))
        spawn.model.setTag("type", "spawn")
        globals.picker.makePickable(spawn.model)
        globals.spawn_list.append(spawn)


    # Initializes a spawn object with database values
    def InitSpawnData(self, spawn, row):
        spawn.spawngroup_id = row["Spawngroup_id"]
        spawn.spawngroup_name = row["spawngroup_name"]
        spawn.spawngroup_minx = row["Spawngroup_minX"]
        spawn.spawngroup_maxx= row["Spawngroup_maxX"]
        spawn.spawngroup_miny = row["Spawngroup_minY"]
        spawn.spawngroup_maxy = row["Spawngroup_maxY"]
        spawn.spawngroup_dist = row["Spawngroup_dist"]
        spawn.spawngroup_mindelay = row["Spawngroup_mindelay"]
        spawn.spawngroup_delay = row["Spawngroup_delay"]
        spawn.spawngroup_despawn = row["Spawngroup_despawntimer"]
        spawn.spawngroup_despawntimer = row["Spawngroup_despawntimer"]
        spawn.spawngroup_spawnlimit = row["Spawngroup_spawnlimit"]


        spawn.spawnentry_id = row["Spawn2Id"]
        spawn.spawnentry_npcid = row["NpcId"]
        spawn.spawnentry_npcname = row["NpcName"]
        spawn.spawnentry_chance = row["Spawnentry_chance"]
        spawn.spawnentry_x = row["Spawn2X"]
        spawn.spawnentry_y = row["Spawn2Y"]
        spawn.spawnentry_z = row["Spawn2Z"]
        spawn.spawnentry_heading = row["Spawn2Heading"]
        spawn.spawnentry_respawn = row["Spawn2Respawn"]
        spawn.spawnentry_variance = row["Spawn2Variance"]
        spawn.spawnentry_pathgrid = row["Spawn2Grid"]
        spawn.spawnentry_condition = row["Spawn2Condition"]
        spawn.spawnentry_condvalue = row["Spawn2CondValue"]
        spawn.spawnentry_version = row["Spawn2Version"]
        spawn.spawnentry_enabled = row["Spawn2Enabled"]
        spawn.spawnentry_animation = row["Spawn2Animation"]
        spawn.spawnentry_zone = row["Spawn2Zone"]

        spawn.spawnentry_originalx = row["Spawn2X"]
        spawn.spawnentry_originaly = row["Spawn2Y"]
        spawn.spawnentry_originalz = row["Spawn2Z"]
        spawn.spawnentry_originalheading = row["Spawn2Heading"]


    # Initializes the camera position upon startup
    def InitCameraPosition(self):
        world.campos = Point3(-155.6, 41.2, 4.9 + world.eyeHeight)
        world.camHeading = 270.0

        base.camera.setPos(world.campos)

    def GetCamera(self):
        return base.camera
Exemple #5
0
def main():
    '''(NoneType) -> NoneType'''
    WINDOW_W, WINDOW_H = get_window_dim()

    pygame.init()

    fps_clock = time.Clock()
    FPS = get_fps()

    window = display.set_mode((WINDOW_W, WINDOW_H))

    # Set up arg parser to get the zone
    arg_parser = ArgumentParser()
    # Add the zone argument
    arg_parser.add_argument("zone", help="the zone to play, relative to data/zone")
    # Get the arg
    args = arg_parser.parse_args()

    ZONE = args.zone

    zone = Zone()
    zone.load(ZONE) # This loads the player

    sum_tracker = SumTracker(zone.player)
    keys_tracker = KeysTracker(zone.player, sum_tracker) # Put it below sum_tracker

    zone.render(window)
    sum_tracker.render(window)
    keys_tracker.render(window)

    display.flip()

    running = True
    while running:
        for event in pygame.event.get():
            if event.type == QUIT:
                running = False
            # Check if player won
            elif zone.player.win:
                # Render text to screen saying player won
                FONT_SIZE = 50
                WHITE = (255, 255, 255)
                BLACK = (0, 0, 0)
                FONT = Font(None, FONT_SIZE)
                X1, Y1 = 40, 100
                X2, Y2 = 40, 150
                FONT_S1 = FONT.render("YOU WIN!", False, WHITE)
                FONT_S2 = FONT.render("Click to exit...", False, WHITE)
                # Black out window
                window.fill(BLACK)
                # Render text
                window.blit(FONT_S1, (X1, Y1))
                window.blit(FONT_S2, (X2, Y2))
                
                display.flip()

                # Wait for a click
                new_event = pygame.event.wait() # Some issue with other events getting in
                                                # way, but it seems to work well enough
                while new_event.type != MOUSEBUTTONDOWN:
                    new_event = pygame.event.wait()

                running = False
            # Check for game over
            elif (zone.player.sum < -128) or (zone.player.sum > 127):
                # Game over
                # Render text to screen saying game is over
                FONT_SIZE = 50
                WHITE = (255, 255, 255)
                BLACK = (0, 0, 0)
                FONT = Font(None, FONT_SIZE)
                X1, Y1 = 40, 100
                X2, Y2 = 40, 150
                X3, Y3 = 40, 250
                # Check for underflow or overflow
                if zone.player.sum < -128:
                    status = "UNDERFLOW"
                else:
                    status = "OVERFLOW"
                FONT_S1 = FONT.render("GAMEOVER!", False, WHITE)
                FONT_S2 = FONT.render("YOU CREATED AN {}!".format(status), False, WHITE)
                FONT_S3 = FONT.render("Click to exit...", False, WHITE)
                # Black out window
                window.fill(BLACK)
                # Render text
                window.blit(FONT_S1, (X1, Y1))
                window.blit(FONT_S2, (X2, Y2))
                window.blit(FONT_S3, (X3, Y3))

                display.flip()

                # Wait for a click
                new_event = pygame.event.wait() # Some issue with other events getting in
                                                # way, but it seems to work well enough
                while new_event.type != MOUSEBUTTONDOWN:
                    new_event = pygame.event.wait()

                running = False

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    zone.move_player("LEFT", window)
                elif event.key == pygame.K_RIGHT:
                    zone.move_player("RIGHT", window)
                elif event.key == pygame.K_UP:
                    zone.move_player("UP", window)
                elif event.key == pygame.K_DOWN:
                    zone.move_player("DOWN", window)
                # Update trackers
                sum_tracker.render(window)
                keys_tracker.render(window)

                display.flip()

        fps_clock.tick(FPS)
    pygame.quit()