def getMouseRay(self, collRay=False): ray = CollisionRay() ray.setFromLens(self.camNode, self.getMouse()) if collRay: return ray else: return Ray(ray.getOrigin(), ray.getDirection())
class DPlayer(DistributedObject): def __init__(self, cr): DistributedObject.__init__(self, cr) self.name = "Player" self.piece = None self.pickerQueue = CollisionHandlerQueue() self.pickerRay = CollisionRay() pickerNode = CollisionNode('mouseRay') pickerNode.setFromCollideMask(BitMask32(0x80)) pickerNode.addSolid(self.pickerRay) self.pickerNP = base.camera.attachNewNode(pickerNode) base.cTrav.addCollider(self.pickerNP, self.pickerQueue) self.accept("mouse1", self.checkClick) def delete(self): """Cleanup just before the object gets deleted""" self.ignoreAll() base.cTrav.removeCollider(self.pickerNP) self.pickerNP.removeNode() if self.piece is not None: self.piece.sendDeleteMsg() DistributedObject.delete(self) def d_getName(self): self.sendUpdate("requestName") def setName(self, newName): self.name = newName base.messenger.send(self.cr.uniqueName("setPlayerName"), [self.name]) def setPiece(self, piece): self.piece = piece def checkClick(self): """Check if the player has clicked on a field and send a request to move to it to the server.""" if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.x, mpos.y) base.cTrav.traverse(render) if self.pickerQueue.getNumEntries() > 0: self.pickerQueue.sortEntries() pickedObj = self.pickerQueue.getEntry(0).getIntoNodePath() fieldName = pickedObj.getParent().getName() base.messenger.send("requestMoveToField", [fieldName]) def d_updateInventory(self): self.sendUpdate("updateInventory") def doUpdateInventory(self, level, inventoryDir): base.messenger.send("updateInventory", [level, inventoryDir]) def doUpdatePotions(self, numPotions): base.messenger.send("updateHealthPotions", [numPotions])
class Mouse(DirectObject): def __init__(self, levelNP): self.setCursor() # store the nodepath to the level collisions # will be used to check for intersections with the mouse ray self.levelNP = levelNP # Setup a traverser for the picking collisions self.picker = CollisionTraverser() # Setup mouse ray self.pq = CollisionHandlerQueue() # Create a collision Node pickerNode = CollisionNode('MouseRay') # set the nodes collision bitmask pickerNode.setFromCollideMask(BitMask32.bit(1))#GeomNode.getDefaultCollideMask()) # create a collision ray self.pickerRay = CollisionRay() # add the ray as a solid to the picker node pickerNode.addSolid(self.pickerRay) # create a nodepath with the camera to the picker node self.pickerNP = base.camera.attachNewNode(pickerNode) # add the nodepath to the base traverser self.picker.addCollider(self.pickerNP, self.pq) def setCursor(self): base.win.clearRejectedProperties() props = WindowProperties() if sys.platform.startswith('linux'): props.setCursorFilename("./assets/cursor.x11") else: props.setCursorFilename("./assets/cursor.ico") base.win.requestProperties(props) def getMousePos(self): # check if we have a mouse on the window if base.mouseWatcherNode.hasMouse(): # get the mouse position on the screen mpos = base.mouseWatcherNode.getMouse() # set the ray's position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) # Now call the traverse function to let the traverser check for collisions # with the added colliders and the levelNP self.picker.traverse(self.levelNP) # check if we have a collision if self.pq.getNumEntries() > 0: # sort the entries to get the closest first self.pq.sortEntries() # This is the point at where the mouse ray and the level plane intersect hitPos = self.pq.getEntry(0).getSurfacePoint(render) return hitPos return Point3(0, 0, 0) task.cont
class MouseCollision: def __init__(self, game): self.game = game self.c_trav = CollisionTraverser() self.mouse_groundHandler = CollisionHandlerQueue() self.mouse_ground_ray = CollisionRay() self.mouse_ground_col = CollisionNode('mouseRay') self.mouse_ground_ray.setOrigin(0, 0, 0) self.mouse_ground_ray.setDirection(0, -1, 0) self.mouse_ground_col.addSolid(self.mouse_ground_ray) self.mouse_ground_col.setFromCollideMask(CollideMask.bit(0)) self.mouse_ground_col.setIntoCollideMask(CollideMask.allOff()) self.mouse_ground_col_np = self.game.camera.attachNewNode( self.mouse_ground_col) self.c_trav.addCollider(self.mouse_ground_col_np, self.mouse_groundHandler) self.game.taskMgr.add(self.update, 'updateMouse') def update(self, task): if self.game.mouseWatcherNode.hasMouse(): if self.game.ship.model: mouse_pos = self.game.mouseWatcherNode.getMouse() self.mouse_ground_ray.setFromLens(self.game.camNode, mouse_pos.getX(), mouse_pos.getY()) near_point = render.getRelativePoint( self.game.camera, self.mouse_ground_ray.getOrigin()) near_vec = render.getRelativeVector( self.game.camera, self.mouse_ground_ray.getDirection()) self.game.ship.shipPoint.setPos( self.PointAtY(self.game.ship.model.getY(), near_point, near_vec)) return task.cont def PointAtY(self, y, point, vec): return point + vec * ((y - point.getY()) / vec.getY())
class Mouse(object): def __init__(self, base): self.base = base if settings.mouse_over: taskMgr.add(self.mouse_task, 'mouse-task') self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = self.base.cam.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask( CollisionNode.getDefaultCollideMask() | GeomNode.getDefaultCollideMask()) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) self.over = None def find_over(self): over = None if self.base.mouseWatcherNode.hasMouse(): mpos = self.base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(self.base.camNode, mpos.getX(), mpos.getY()) self.picker.traverse(render) if self.pq.getNumEntries() > 0: self.pq.sortEntries() np = self.pq.getEntry(0).getIntoNodePath().findNetPythonTag( 'owner') owner = np.getPythonTag('owner') over = owner np = self.pq.getEntry(0).getIntoNodePath().findNetPythonTag( 'patch') if np is not None: self.patch = np.getPythonTag('patch') else: self.patch = None return over def get_over(self): if settings.mouse_over: over = self.over else: over = self.find_over() return over def mouse_task(self, task): if self.mouseWatcherNode.hasMouse(): self.over = self.find_over() return Task.cont
class MouseCollision: def __init__(self, game): self.game = game self.c_trav = CollisionTraverser() self.mouse_groundHandler = CollisionHandlerQueue() self.mouse_ground_ray = CollisionRay() self.mouse_ground_col = CollisionNode('mouseRay') self.mouse_ground_ray.setOrigin(0, 0, 0) self.mouse_ground_ray.setDirection(0, -1, 0) self.mouse_ground_col.addSolid(self.mouse_ground_ray) self.mouse_ground_col.setFromCollideMask(CollideMask.bit(0)) self.mouse_ground_col.setIntoCollideMask(CollideMask.allOff()) self.mouse_ground_col_np = self.game.camera.attachNewNode(self.mouse_ground_col) self.c_trav.addCollider(self.mouse_ground_col_np, self.mouse_groundHandler) self.game.taskMgr.add(self.update, 'updateMouse') def update(self, task): if self.game.mouseWatcherNode.hasMouse(): if self.game.ship.model: mouse_pos = self.game.mouseWatcherNode.getMouse() self.mouse_ground_ray.setFromLens(self.game.camNode, mouse_pos.getX(), mouse_pos.getY()) near_point = render.getRelativePoint(self.game.camera, self.mouse_ground_ray.getOrigin()) near_vec = render.getRelativeVector(self.game.camera, self.mouse_ground_ray.getDirection()) self.game.ship.shipPoint.setPos(self.PointAtY(self.game.ship.model.getY(), near_point, near_vec)) return task.cont def PointAtY(self, y, point, vec): return point + vec * ((y - point.getY()) / vec.getY())
def _get_collision(self, node, debug=False): mx = self.mouseWatcherNode.getMouseX() my = self.mouseWatcherNode.getMouseY() if debug: print "mouse:", (mx, my) # get the origin and direction of the ray extending from the # camera to the mouse pointer cm = np.array(self.cam.getNetTransform().getMat()) cr = CollisionRay() cr.setFromLens(self.cam.node(), (mx, my)) cp = np.hstack([cr.getOrigin(), 1]) cd = np.hstack([cr.getDirection(), 0]) cp = np.dot(cm.T, cp)[:3] cd = np.dot(cm.T, cd)[:3] if cd[2] > -1: cd[2] = -1 if debug: print "direction:", cd print "origin:", cp # point on the plane, z-axis pz = node.getPos(self.render)[2] sz = node.getScale(self.render)[2] / 2.0 p0 = np.array([0, 0, pz + sz]) if debug: print "p0:", p0 # this is the intersection equation that we want to solve, # where s is the point on the line that intersects # e_z(cp + s*cd - p0) = 0 s = (p0[2] - cp[2]) / cd[2] if debug: print "s:", s # transform the collision point from line coordinates to world # coordinates cv = cp + s * cd if debug: print "collision:", cv return cv
class MouseHandler: def __init__(self, camera, level): self.camera = camera self.level = level self.handler = CollisionHandlerQueue() self.traverser = CollisionTraverser('traverser') pickerNode = CollisionNode('mouseRay') pickerNP = self.camera.attachNewNode(pickerNode) pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.pickerRay = CollisionRay() pickerNode.addSolid(self.pickerRay) self.traverser.addCollider(pickerNP, self.handler) def pick_node(self, mpos): self.pickerRay.setFromLens(self.camera.getChild(0).node(), mpos.getX(), mpos.getY()) self.traverser.traverse(self.level) if self.handler.getNumEntries() > 0: self.handler.sortEntries() node = self.handler.getEntry(0).getIntoNodePath() if not node.isEmpty(): return node
class DistributedLevel(DistributedObject): """ An instance of these is created and placed in the middle of the zone. It serves to illustrate the creation of AI-side objects to populate the world, and a general mechanism for making them react to the avatars. """ def __init__(self, cr): DistributedObject.__init__(self, cr) #self.model = loader.loadModel('environment') #self.model.setZ(0) #self.builder = Builder(self, "map.txt", "development") plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) cnode = CollisionNode('cnode') cnode.setIntoCollideMask(BitMask32.bit(1)) cnode.setFromCollideMask(BitMask32.bit(1)) cnode.addSolid(plane) self.planeNP = self.model.attachNewNode(cnode) self.planeNP.show() # Setup a traverser for the picking collisions self.picker = CollisionTraverser() # Setup mouse ray self.pq = CollisionHandlerQueue() # Create a collision Node pickerNode = CollisionNode('MouseRay') # set the nodes collision bitmask pickerNode.setFromCollideMask(BitMask32.bit(1)) # create a collision ray self.pickerRay = CollisionRay() # add the ray as a solid to the picker node pickerNode.addSolid(self.pickerRay) # create a nodepath with the camera to the picker node self.pickerNP = base.camera.attachNewNode(pickerNode) # add the nodepath to the base traverser self.picker.addCollider(self.pickerNP, self.pq) print "model loaded" #TODO: check how to load multiple levels and set players in specific levels! self.accept("mouse1", self.mouseClick) def announceGenerate(self): """ This method is called after generate(), after all of the required fields have been filled in. At the time of this call, the distributed object is ready for use. """ DistributedObject.announceGenerate(self) # Now that the object has been fully manifested, we can parent # it into the scene. print "render the model" self.model.reparentTo(render) def disable(self): # Take it out of the scene graph. self.detachNode() DistributedObject.disable(self) def checkMousePos(self, mpos, camPos, camHpr): lens = LenseNode.copyLens(base.camNode) lens.setPos(camPos) lens.setHpr(camHpr) help(mpos) mpos = Point2(mpos.x, mpos.y) pos = self.getClickPosition(mpos, lens) print "mouse clicked at:", pos def mouseClick(self): """Send an event to the server that will check where the mouse will hit and what action needs to be done""" hitPos = (0, 0, 0) # check if we have a mouse on the window if base.mouseWatcherNode.hasMouse(): # get the mouse position on the screen mpos = base.mouseWatcherNode.getMouse() print mpos # set the ray's position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) # Now call the traverse function to let the traverser check for collisions # with the added colliders and the levelNP self.picker.traverse(self.planeNP) # check if we have a collision if self.pq.getNumEntries() > 0: # sort the entries to get the closest first self.pq.sortEntries() # This is the point at where the mouse ray and the level plane intersect hitPos = self.pq.getEntry(0).getSurfacePoint(render) base.messenger.send("clickPosition", [hitPos])
class PoolBallGame(ShowBase): def __init__(self): # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) base.setBackgroundColor(0, 0, 0) self.accept("escape", sys.exit) # Escape quits self.disableMouse() camera.setPosHpr(0, 0, 0, 0, 0, 0) lens = PerspectiveLens() lens.setFov(90, 60) lens.setNear(0.01) lens.setFar(100000) self.cam.node().setLens(lens) self.ballSize = 0.025 self.cueLength = 0.2 # self.ballRoot = render.attachNewNode("ballRoot") # #self.ball = loader.loadModel("models/ball") # self.ball = loader.loadModel("models/ball_0_center.egg") # #self.ball = loader.loadModel("models/ball.dae") # self.ball.setScale(ballSize, ballSize, ballSize) # self.ball.reparentTo(self.ballRoot) # #print(self.ball.getBounds()) # #exit(1) # #self.ballSphere = self.ball.find("**/ball") # #print(self.ball.getScale()[0]) # cs = CollisionSphere(0, 0, 0, 1) # self.ballSphere = self.ball.attachNewNode(CollisionNode('ball')) # self.ballSphere.node().addSolid(cs) # self.ballSphere.node().setFromCollideMask(BitMask32.bit(0)) # self.ballSphere.node().setIntoCollideMask(BitMask32.bit(1)) self.sceneIndex = 0 self.planeInfo = PlaneScene(self.sceneIndex) self.planeScene = self.planeInfo.generateEggModel() self.planeScene.setTwoSided(True) self.planeScene.reparentTo(render) self.planeScene.hide() #get geometries from plane predictions planeTriangles, horizontalPlaneTriangles, self.gravityDirection = self.planeInfo.getPlaneTriangles( ) # add pool balls self.ballRoots = [] self.balls = [] self.ballSpheres = [] self.ballGroundRays = [] for ballIndex in xrange(3): ballRoot = render.attachNewNode("ballRoot_" + str(ballIndex)) ball = loader.loadModel("models/ball_" + str(ballIndex) + "_center.egg") ball.setScale(self.ballSize, self.ballSize, self.ballSize) cs = CollisionSphere(0, 0, 0, 1) ballSphere = ball.attachNewNode( CollisionNode('ball_' + str(ballIndex))) ballSphere.node().addSolid(cs) ballSphere.node().setFromCollideMask( BitMask32.bit(0) | BitMask32.bit(1) | BitMask32.bit(3) | BitMask32.bit(4)) ballSphere.node().setIntoCollideMask(BitMask32.bit(1)) ball.reparentTo(ballRoot) self.ballRoots.append(ballRoot) self.balls.append(ball) self.ballSpheres.append(ballSphere) ballGroundRay = CollisionRay() # Create the ray ballGroundRay.setOrigin(0, 0, 0) # Set its origin ballGroundRay.setDirection( self.gravityDirection[0], self.gravityDirection[1], self.gravityDirection[2]) # And its direction # Collision solids go in CollisionNode # Create and name the node ballGroundCol = CollisionNode('ball_ray_' + str(ballIndex)) ballGroundCol.addSolid(ballGroundRay) # Add the ray ballGroundCol.setFromCollideMask( BitMask32.bit(2)) # Set its bitmasks ballGroundCol.setIntoCollideMask(BitMask32.allOff()) # Attach the node to the ballRoot so that the ray is relative to the ball # (it will always be 10 feet over the ball and point down) ballGroundColNp = ballRoot.attachNewNode(ballGroundCol) self.ballGroundRays.append(ballGroundColNp) ballRoot.hide() continue # Finally, we create a CollisionTraverser. CollisionTraversers are what # do the job of walking the scene graph and calculating collisions. # For a traverser to actually do collisions, you need to call # traverser.traverse() on a part of the scene. Fortunately, ShowBase # has a task that does this for the entire scene once a frame. By # assigning it to self.cTrav, we designate that this is the one that # it should call traverse() on each frame. self.cTrav = CollisionTraverser() # Collision traversers tell collision handlers about collisions, and then # the handler decides what to do with the information. We are using a # CollisionHandlerQueue, which simply creates a list of all of the # collisions in a given pass. There are more sophisticated handlers like # one that sends events and another that tries to keep collided objects # apart, but the results are often better with a simple queue self.cHandler = CollisionHandlerQueue() # Now we add the collision nodes that can create a collision to the # traverser. The traverser will compare these to all others nodes in the # scene. There is a limit of 32 CollisionNodes per traverser # We add the collider, and the handler to use as a pair #self.cTrav.addCollider(self.ballSphere, self.cHandler) for ballSphere in self.ballSpheres: self.cTrav.addCollider(ballSphere, self.cHandler) continue for ballGroundRay in self.ballGroundRays: self.cTrav.addCollider(ballGroundRay, self.cHandler) continue #self.cTrav.addCollider(self.ballGroundColNp, self.cHandler) # Collision traversers have a built in tool to help visualize collisions. # Uncomment the next line to see it. #self.cTrav.showCollisions(render) # This section deals with lighting for the ball. Only the ball was lit # because the maze has static lighting pregenerated by the modeler ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.55, .55, .55, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(LVector3(0, 0, -1)) directionalLight.setColor((0.375, 0.375, 0.375, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) for ballRoot in self.ballRoots: ballRoot.setLight(render.attachNewNode(ambientLight)) ballRoot.setLight(render.attachNewNode(directionalLight)) continue # This section deals with adding a specular highlight to the ball to make # it look shiny. Normally, this is specified in the .egg file. m = Material() m.setSpecular((1, 1, 1, 1)) m.setShininess(96) for ball in self.balls: ball.setMaterial(m, 1) continue # self.original = False # if self.original: # camera.setPosHpr(0, 0, 25, 0, -90, 0) # self.maze = loader.loadModel("models/maze") # self.maze.reparentTo(render) # self.walls = self.maze.find("**/wall_collide") # self.walls.node().setIntoCollideMask(BitMask32.bit(0)) # self.walls.show() # pass # create collision entities from plane predictions self.triNPs = [] for triangleIndex, triangle in enumerate(planeTriangles): #print(triangleIndex) #for triangle in triangles: #print(triangle) tri = CollisionPolygon( Point3(triangle[0][0], triangle[0][1], triangle[0][2]), Point3(triangle[1][0], triangle[1][1], triangle[1][2]), Point3(triangle[2][0], triangle[2][1], triangle[2][2])) triNP = render.attachNewNode( CollisionNode('tri_' + str(triangleIndex))) triNP.node().setIntoCollideMask(BitMask32.bit(0)) triNP.node().addSolid(tri) self.triNPs.append(triNP) #triNP.show() continue # create special collision entities for horizontal planes so that balls can fall on one horizontal plane and bounce on it for triangleIndex, triangle in enumerate(horizontalPlaneTriangles): #print(triangleIndex) #for triangle in triangles: #print(triangle) tri = CollisionPolygon( Point3(triangle[0][0], triangle[0][1], triangle[0][2]), Point3(triangle[1][0], triangle[1][1], triangle[1][2]), Point3(triangle[2][0], triangle[2][1], triangle[2][2])) triNP = render.attachNewNode( CollisionNode('ground_' + str(triangleIndex))) triNP.node().setIntoCollideMask(BitMask32.bit(2)) triNP.node().addSolid(tri) self.triNPs.append(triNP) #triNP.show() continue # tri = CollisionPolygon(Point3(-1, 4, -1), Point3(2, 4, -1), Point3(2, 4, 2)) # triNP = render.attachNewNode(CollisionNode('tri')) # triNP.node().setIntoCollideMask(BitMask32.bit(0)) # triNP.node().addSolid(tri) # triNP.show() #self.planeScene.node().setIntoCollideMask(BitMask32.bit(0)) # roomRootNP = self.planeScene # roomRootNP.flattenLight() # mesh = BulletTriangleMesh() # polygons = roomRootNP.findAllMatches("**/+GeomNode") # # p0 = Point3(-10, 4, -10) # # p1 = Point3(-10, 4, 10) # # p2 = Point3(10, 4, 10) # # p3 = Point3(10, 4, -10) # # mesh.addTriangle(p0, p1, p2) # # mesh.addTriangle(p1, p2, p3) # print(polygons) # for polygon in polygons: # geom_node = polygon.node() # #geom_node.reparentTo(self.render) # #print(geom_node.getNumGeoms()) # ts = geom_node.getTransform() # #print(ts) # for geom in geom_node.getGeoms(): # mesh.addGeom(geom, ts) # continue # continue # #self.scene = roomRootNP # shape = BulletTriangleMeshShape(mesh, dynamic=False) # #shape = BulletPlaneShape(Vec3(0, 0, 1), 1) # room = BulletRigidBodyNode('scene') # room.addShape(shape) # #room.setLinearDamping(0.0) # #room.setFriction(0.0) # print(shape) # room.setDeactivationEnabled(False) # roomNP = render.attachNewNode(room) # roomNP.setPos(0, 0, 0) # roomNP.node().setIntoCollideMask(BitMask32.bit(0)) # self.world = BulletWorld() # self.world.setGravity(Vec3(0, 0, 0)) # self.world.attachRigidBody(roomNP.node()) #room.setRestitution(1) #self.roomNP = self.scene # create the cue self.cueRoot = render.attachNewNode("cueRoot") self.cue = loader.loadModel("models/cue_center.egg") self.cue.setScale(self.cueLength * 3, self.cueLength * 3, self.cueLength) self.cue.reparentTo(self.cueRoot) self.cuePos = (10, 0, 0) self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be positioned # relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other # collision we could separate it self.pickerNode.setFromCollideMask(BitMask32.bit(2)) self.pickerNode.setIntoCollideMask(BitMask32.allOff()) self.pickerRay = CollisionRay() # Make our ray # Add it to the collision node self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.cTrav.addCollider(self.pickerNP, self.cHandler) self.accept("mouse1", self.hit) # left-click grabs a piece # create holes self.holeLength = 0.06 holePos, holeHpr = self.planeInfo.getHolePos() self.holeRoot = render.attachNewNode("holeRoot") #self.hole = loader.loadModel("models/hole_horizontal_center.egg") self.hole = loader.loadModel("models/hole_color.egg") #self.hole = loader.loadModel("models/billiards_hole_center.egg") self.hole.setScale(self.holeLength, self.holeLength, self.holeLength) self.hole.reparentTo(self.holeRoot) self.hole.setTwoSided(True) self.holeRoot.setPos(holePos[0], holePos[1], holePos[2]) self.holeRoot.setHpr(holeHpr[0], holeHpr[1], holeHpr[2]) #tex = loader.loadTexture('models/Black_Hole.jpg') #self.hole.setTexture(tex, 1) self.holeRoot.hide() ct = CollisionTube(0, 0, 0, 0, 0.001, 0, 0.5) self.holeTube = self.hole.attachNewNode(CollisionNode('hole')) self.holeTube.node().addSolid(ct) self.holeTube.node().setFromCollideMask(BitMask32.allOff()) self.holeTube.node().setIntoCollideMask(BitMask32.bit(4)) #self.holeTube.show() # create portals inPortalPos, inPortalHpr, outPortalPos, outPortalHpr, self.portalNormal = self.planeInfo.getPortalPos( ) self.portalLength = 0.06 self.inPortalRoot = render.attachNewNode("inPortalRoot") self.inPortal = loader.loadModel("models/portal_2_center.egg") self.inPortal.setScale(self.portalLength, self.portalLength, self.portalLength) self.inPortal.reparentTo(self.inPortalRoot) self.inPortalRoot.setPos(inPortalPos[0], inPortalPos[1], inPortalPos[2]) self.inPortalRoot.setHpr(inPortalHpr[0], inPortalHpr[1], inPortalHpr[2]) self.inPortalRoot.hide() ct = CollisionTube(0, 0, 0, 0, 0.001, 0, 1) self.inPortalTube = self.inPortal.attachNewNode( CollisionNode('portal_in')) self.inPortalTube.node().addSolid(ct) self.inPortalTube.node().setFromCollideMask(BitMask32.allOff()) self.inPortalTube.node().setIntoCollideMask(BitMask32.bit(3)) #self.inPortalTube.hide() self.outPortalRoot = render.attachNewNode("outPortalRoot") self.outPortal = loader.loadModel("models/portal_2_center.egg") self.outPortal.setScale(self.portalLength, self.portalLength, self.portalLength) self.outPortal.reparentTo(self.outPortalRoot) self.outPortalRoot.setPos(outPortalPos[0], outPortalPos[1], outPortalPos[2]) self.outPortalRoot.setHpr(outPortalHpr[0], outPortalHpr[1], outPortalHpr[2]) self.outPortalRoot.hide() ct = CollisionTube(0, 0, 0, 0, 0.001, 0, 1) self.outPortalTube = self.outPortal.attachNewNode( CollisionNode('portal_out')) self.outPortalTube.node().addSolid(ct) self.outPortalTube.node().setFromCollideMask(BitMask32.allOff()) self.outPortalTube.node().setIntoCollideMask(BitMask32.bit(3)) #self.outPortalTube.hide() #self.inPortalTube.show() #self.outPortalTube.show() #self.holeTube.show() #self.cTrav.addCollider(self.holeTube, self.cHandler) # create background image background_image = loader.loadTexture('dump/' + str(self.sceneIndex) + '_image.png') cm = CardMaker('background') cm.setHas3dUvs(True) info = np.load('dump/' + str(self.sceneIndex) + '_info.npy') #self.camera = getCameraFromInfo(self.info) depth = 10.0 sizeU = info[2] / info[0] * depth sizeV = info[6] / info[5] * depth cm.setFrame(Point3(-sizeU, depth, -sizeV), Point3(sizeU, depth, -sizeV), Point3(sizeU, depth, sizeV), Point3(-sizeU, depth, sizeV)) self.card = self.render.attachNewNode(cm.generate()) self.card.setTransparency(True) self.card.setTexture(background_image) self.card.hide() self.ballGroundMap = {} self.ballBouncing = np.full(len(self.balls), 3) self.started = False self.start() #self.hitIndex = -1 self.showing = 'parts' self.showingProgress = 0 partsScene = PartsScene(self.sceneIndex) self.planeNPs, self.planeCenters = partsScene.generateEggModel() return def start(self): #startPos = self.maze.find("**/start").getPos() #self.ballRoot.setPos(0.5, 0, 0) #self.ballV = LVector3(0, 0.5, 0) # Initial velocity is 0 #self.accelV = LVector3(0, 0, 0) # Initial acceleration is 0 self.ballVs = [] self.accelVs = [] for ballIndex in xrange(len(self.balls)): self.ballVs.append(LVector3(0, 0, 0)) self.accelVs.append(LVector3(0, 0, 0)) continue self.ballRoots[0].setPos(0.2, 1.05, -0.1) #self.ballVs[0] = LVector3(0, 0.0, 0) self.ballRoots[1].setPos(0.32, 1.2, -0.1) #self.ballRoots[2].setHpr(0, 0, 90) self.ballRoots[2].setPos(-0.4, 1.1, 0.4) axis = LVector3.up() prevRot = LRotationf(self.balls[2].getQuat()) newRot = LRotationf(axis, 90) self.balls[2].setQuat(prevRot * newRot) # Create the movement task, but first make sure it is not already # running taskMgr.remove("rollTask") #taskMgr.remove("mouseTask") self.mainLoop = taskMgr.add(self.rollTask, "rollTask") #self.mainLoop = taskMgr.add(self.mouseTask, "mouseTask") return def hit(self): if abs(self.cuePos[0]) < 5: # direction and strength based on mouse position cueDirection = self.ballRoots[0].getPos() - LVector3( self.cuePos[0], self.cuePos[1], self.cuePos[2]) power = cueDirection.length() cueDirection = cueDirection / cueDirection.length() self.ballVs[0] = cueDirection * np.sqrt(self.cuePower) # elif self.hitIndex == 0: # self.ballVs[0] = LVector3(0.5, 0.47, 0) # self.hitIndex += 1 # elif self.hitIndex == 1: # self.ballVs[0] = LVector3(0.072, 0.62, 0) # self.hitIndex += 1 # elif self.hitIndex == 2: # self.ballVs[0] = LVector3(0.7, 0.0, 0) # self.hitIndex += 1 # pass self.started = True print('hit', cueDirection) self.ballBouncing = np.full(len(self.balls), 3) pass # This function handles the collision between the ball and a wall def planeCollideHandler(self, colEntry): #return ballName = colEntry.getFromNode().getName() ballIndex = int(ballName[5:]) # First we calculate some numbers we need to do a reflection # print(colEntry) # name = colEntry.getIntoNode().getName() # triangleIndex = int(name[4:]) # print(triangleIndex) # print(self.planeNormals[triangleIndex]) # print(colEntry.getSurfaceNormal(render)) # exit(1) norm = colEntry.getSurfaceNormal(render) * -1 # The normal of the wall norm.normalize() curSpeed = self.ballVs[ballIndex].length() # The current speed inVec = self.ballVs[ballIndex] / curSpeed # The direction of travel velAngle = norm.dot(inVec) # Angle of incidance hitDir = colEntry.getSurfacePoint( render) - self.ballRoots[ballIndex].getPos() hitDir.normalize() # The angle between the ball and the normal hitAngle = norm.dot(hitDir) # Ignore the collision if the ball is either moving away from the wall # already (so that we don't accidentally send it back into the wall) # and ignore it if the collision isn't dead-on (to avoid getting caught on # corners) #print(velAngle, hitAngle) if velAngle > 0 and hitAngle > .995: print('plane', ballName, velAngle) # Standard reflection equation reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec # This makes the velocity half of what it was if the hit was dead-on # and nearly exactly what it was if this is a glancing blow #self.ballVs[ballIndex] = reflectVec * (curSpeed * (((1 - velAngle) * .5) + .5)) self.ballVs[ballIndex] = reflectVec * curSpeed # Since we have a collision, the ball is already a little bit buried in # the wall. This calculates a vector needed to move it so that it is # exactly touching the wall disp = (colEntry.getSurfacePoint(render) - colEntry.getInteriorPoint(render)) newPos = self.ballRoots[ballIndex].getPos() + disp self.ballRoots[ballIndex].setPos(newPos) pass return # This function handles the collision between the ball and a wall def portal(self, colEntry): ballName = colEntry.getFromNode().getName() print('portal', ballName) ballIndex = int(ballName[5:]) #norm = colEntry.getSurfaceNormal(render) * -1 # The normal of the wall norm = LVector3(self.portalNormal[0], self.portalNormal[1], self.portalNormal[2]) norm.normalize() curSpeed = self.ballVs[ballIndex].length() # The current speed inVec = self.ballVs[ballIndex] / curSpeed # The direction of travel velAngle = norm.dot(inVec) # Angle of incidance hitDir = colEntry.getSurfacePoint( render) - self.ballRoots[ballIndex].getPos() hitDir.normalize() # The angle between the ball and the normal #print(colEntry.getSurfacePoint(render), self.ballRoots[ballIndex].getPos()) #print(norm, hitDir) hitAngle = norm.dot(hitDir) # Ignore the collision if the ball is either moving away from the wall # already (so that we don't accidentally send it back into the wall) # and ignore it if the collision isn't dead-on (to avoid getting caught on # corners) #print(velAngle, hitAngle) #print(velAngle, hitAngle) if velAngle > 0: print(colEntry.getIntoNode().getName()) if '_in' in colEntry.getIntoNode().getName(): self.ballRoots[ballIndex].setPos(self.outPortalRoot.getPos()) else: self.ballRoots[ballIndex].setPos(self.inPortalRoot.getPos()) pass print(self.ballVs[ballIndex], ((norm * norm.dot(inVec * -1) * 2) + inVec) * curSpeed, norm) #exit(1) self.ballVs[ballIndex] = ( (norm * norm.dot(inVec * -1) * 2) + inVec) * curSpeed #self.ballVs[ballIndex] *= -1 pass return # This function handles the collision between the ball and a wall def ballCollideHandler(self, colEntry): # First we calculate some numbers we need to do a reflection fromBallName = colEntry.getFromNode().getName() fromBallIndex = int(fromBallName[5:]) #if fromBallIndex != 0: #return intoBallName = colEntry.getIntoNode().getName() intoBallIndex = int(intoBallName[5:]) print('ball', fromBallName, intoBallName) norm = colEntry.getSurfaceNormal(render) * -1 # The normal of the wall norm = norm / norm.length() curSpeed = self.ballVs[fromBallIndex].length() # The current speed inVec = self.ballVs[fromBallIndex] / curSpeed # The direction of travel velAngle = norm.dot(inVec) # Angle of incidance hitDir = colEntry.getSurfacePoint( render) - self.ballRoots[fromBallIndex].getPos() hitDir.normalize() # The angle between the ball and the normal hitAngle = norm.dot(hitDir) # print(norm) # print(self.ballVs[fromBallIndex]) # print(velAngle, hitAngle) # print(self.ballRoots[fromBallIndex].getPos()) # print(self.ballRoots[intoBallIndex].getPos()) # exit(1) #print(fromBallIndex, intoBallIndex) #exit(1) # Ignore the collision if the ball is either moving away from the wall # already (so that we don't accidentally send it back into the wall) # and ignore it if the collision isn't dead-on (to avoid getting caught on # corners) #print(velAngle, hitAngle) if velAngle > 0 and hitAngle > .995: # Standard reflection equation self.ballVs[fromBallIndex] = ( (norm * norm.dot(inVec * -1)) + inVec) * curSpeed disp = (colEntry.getSurfacePoint(render) - colEntry.getInteriorPoint(render)) newPos = self.ballRoots[fromBallIndex].getPos() + disp self.ballRoots[fromBallIndex].setPos(newPos) self.ballVs[intoBallIndex] = norm * norm.dot(inVec) * curSpeed pass return # the function handles the situation when a ball falls on a lower horizontal plane def groundCollideHandler(self, colEntry): # Set the ball to the appropriate Z value for it to be exactly on the # ground ballName = colEntry.getFromNode().getName() if 'mouseRay' in ballName: for v in self.ballVs: if v.length() > 1e-4: self.cuePos = (10, 0, 0) return continue #print(self.mouseWatcherNode.hasMouse()) norm = colEntry.getSurfaceNormal(render) norm.normalize() touchPoint = colEntry.getSurfacePoint(render) cuePoint = touchPoint + norm * self.ballSize cueDirection = self.ballRoots[0].getPos() - cuePoint self.cuePower = cueDirection.length() cueDirection = cueDirection / cueDirection.length() cuePoint = self.ballRoots[0].getPos( ) - cueDirection * self.cueLength self.cuePos = cuePoint #self.cueRoot.setH(np.rad2deg(np.arctan2(cueDirection[1], cueDirection[0])) + 90) self.cueRoot.setH( np.rad2deg(np.arctan2(cueDirection[1], cueDirection[0])) + 90) self.cueRoot.setP(-np.rad2deg(np.arcsin(cueDirection[2])) + 90) #self.cueRoot.setP(90) #print(np.rad2deg(np.arctan2(cueDirection[1], cueDirection[0])), np.rad2deg(np.arcsin(cueDirection[2]))) # prevRot = LRotationf(self.cue.getQuat()) # axis = LVector3.up().cross(self.ballVs[ballIndex]) # newRot = LRotationf(axis, 45.5 * dt * self.ballVs[ballIndex].length()) # self.balls[ballIndex].setQuat(prevRot * newRot) return #print('ground', ballName) #print(ballName, colEntry.getIntoNode().getName()) #print(colEntry.getFromNode().getBitMask(), colEntry.getIntoNode().getBitMask()) ballIndex = int(ballName[9:]) groundName = colEntry.getIntoNode().getName() groundIndex = int(groundName[7:]) #print(groundIndex) #print(self.ballGroundMap) if ballIndex == 0 and False: print(groundIndex, self.ballGroundMap) pass if ballIndex not in self.ballGroundMap or self.ballGroundMap[ ballIndex][0] != groundIndex: return norm = -colEntry.getSurfaceNormal(render) norm = norm / norm.length() curSpeed = self.ballVs[ballIndex].length() # The current speed inVec = self.ballVs[ballIndex] / max(curSpeed, 1e-4) # The direction of travel velAngle = norm.dot(inVec) # Angle of incidance hitDir = colEntry.getSurfacePoint( render) - self.ballRoots[ballIndex].getPos() hitDir.normalize() # The angle between the ball and the normal hitAngle = norm.dot(hitDir) surfacePos = colEntry.getSurfacePoint(render) ballPos = self.ballRoots[ballIndex].getPos() surfacePos = ballPos + norm * norm.dot(surfacePos - ballPos) distance = norm.dot(surfacePos - ballPos) if distance < 0: return if distance < self.ballSize + 1e-3: self.ballRoots[ballIndex].setPos(surfacePos - norm * self.ballSize) if self.ballVs[ballIndex].length() > 1e-2: self.ballVs[ballIndex] = (-norm * velAngle + inVec) * curSpeed #self.ballVs[ballIndex] = -norm.cross(norm.cross(self.ballVs[ballIndex])) self.accelVs[ballIndex] = -self.ballVs[ ballIndex] / self.ballVs[ballIndex].length() * 0.0025 else: self.ballVs[ballIndex] = LVector3(0, 0, 0) self.accelVs[ballIndex] = LVector3(0, 0, 0) pass else: self.accelVs[ ballIndex] = self.accelVs[ballIndex] - norm * norm.dot( self.accelVs[ballIndex]) + norm * 0.05 pass return # if self.started: # if abs(distance - self.ballSize) > 0.001 and abs(distance - self.ballSize) < self.ballSize: # self.ballRoots[ballIndex].setPos(surfacePos - norm * self.ballSize) # pass # self.ballVs[ballIndex] = -norm.cross(norm.cross(self.ballVs[ballIndex])) # if self.ballVs[ballIndex].length() > 1e-3: # self.accelVs[ballIndex] = -self.ballVs[ballIndex] / self.ballVs[ballIndex].length() * 0.015 # else: # self.ballVs[ballIndex] = LVector3(0, 0, 0) # self.accelVs[ballIndex] = LVector3(0, 0, 0) # pass # #print(self.ballVs[ballIndex], self.accelVs[ballIndex]) # #print(surfacePos - norm * self.ballSize) # return if ballIndex == 0: print('distance_1', self.started, distance, velAngle, self.ballVs[ballIndex], self.accelVs[ballIndex]) if distance < self.ballSize: self.ballRoots[ballIndex].setPos(surfacePos - norm * self.ballSize) if velAngle > 0 and hitAngle > .995: if abs(velAngle * curSpeed) < 0.2: if ((-norm * velAngle + inVec) * curSpeed).length() < 0.02: self.ballVs[ballIndex] = LVector3(0, 0, 0) self.accelVs[ballIndex] = LVector3(0, 0, 0) pass pass else: if self.ballBouncing[ballIndex] > 0: if ballIndex == 0: print('bouncing') pass self.ballVs[ballIndex] = ( -norm * velAngle + inVec ) * curSpeed * 0.5 - norm * velAngle * curSpeed * 0.25 self.accelVs[ballIndex] = LVector3(0, 0, 0) self.ballBouncing[ballIndex] -= 1 else: self.ballVs[ballIndex] = (-norm * velAngle + inVec) * curSpeed self.accelVs[ballIndex] = LVector3(0, 0, 0) pass pass pass pass if (distance - self.ballSize) > 0.001: self.accelVs[ ballIndex] = self.accelVs[ballIndex] - norm * norm.dot( self.accelVs[ballIndex]) + norm * 0.1 # print(self.accelVs[ballIndex] - norm * norm.dot(self.accelVs[ballIndex])) # print(norm) # print(inVec) # print(velAngle) # print(-norm * velAngle + inVec) # print(norm * 0.01) # exit(1) elif distance - self.ballSize > -0.001: if self.ballVs[ballIndex].length() < 0.001: #print('stop', self.ballVs[ballIndex], self.accelVs[ballIndex]) self.ballVs[ballIndex] = LVector3(0, 0, 0) self.accelVs[ballIndex] = LVector3(0, 0, 0) self.started = False else: if abs(velAngle) < 1e-3: self.ballVs[ballIndex] = (-norm * velAngle + inVec) * curSpeed #self.ballVs[ballIndex] = -norm.cross(norm.cross(self.ballVs[ballIndex])) self.accelVs[ballIndex] = -self.ballVs[ ballIndex] / self.ballVs[ballIndex].length() * 0.01 #print('speed', self.ballVs[ballIndex], self.accelVs[ballIndex]) pass pass pass # #print(distance - self.ballSize) # if (distance - self.ballSize) > 0.01: # self.accelVs[ballIndex] = self.accelVs[ballIndex] - norm * norm.dot(self.accelVs[ballIndex]) + norm * 0.01 # #if ballIndex == 0: # #print(velAngle, self.ballVs[ballIndex], self.accelVs[ballIndex], norm) # #pass # print('fall', self.accelVs[ballIndex], distance) # # print(self.accelVs[ballIndex] - norm * norm.dot(self.accelVs[ballIndex])) # # print(norm) # # print(inVec) # # print(velAngle) # # print(-norm * velAngle + inVec) # # print(norm * 0.01) # # exit(1) # else: # #hitAngle > .995 # #print(velAngle) # #print(norm) # #self.ballRoots[ballIndex].setPos(surfacePos - norm * self.ballSize) # if curSpeed > 1e-1: # print('angle', velAngle, norm) # self.norm = norm # pass # if velAngle > 1e-3: # if curSpeed < 1e-3: # self.ballVs[ballIndex] = LVector3(0, 0, 0) # self.accelVs[ballIndex] = LVector3(0, 0, 0) # self.ballRoots[ballIndex].setPos(surfacePos - norm * self.ballSize) # else: # self.ballVs[ballIndex] = (-norm * velAngle + inVec) * curSpeed * 0.9 - norm * velAngle * curSpeed * 0.25 # self.accelVs[ballIndex] = LVector3(0, 0, 0) # pass # #print((-norm * velAngle + inVec) * curSpeed * 0.9, norm * velAngle * curSpeed * 0.25) # #print(curSpeed, norm.dot(self.ballVs[ballIndex]) / self.ballVs[ballIndex].length(), self.ballVs[ballIndex], self.accelVs[ballIndex]) # #print(norm.dot(self.ballVs[ballIndex]) / self.ballVs[ballIndex].length(), norm.dot(self.accelVs[ballIndex]) / self.accelVs[ballIndex].length(), self.ballVs[ballIndex], self.accelVs[ballIndex]) # elif velAngle > -1e-3: # if self.ballVs[ballIndex].length() < 0.001: # #self.ballVs[ballIndex] = LVector3(0, 0, 0) # #self.accelVs[ballIndex] = LVector3(0, 0, 0) # #print('stop', self.ballVs[ballIndex], self.accelVs[ballIndex]) # pass # else: # #self.ballVs[ballIndex] = (-norm * velAngle + inVec) * curSpeed * 0.9 # #print(self.ballVs[ballIndex], self.accelVs[ballIndex]) # self.accelVs[ballIndex] = -(-norm * velAngle + inVec) * 0.1 # print('accel', self.accelVs[ballIndex]) # pass # pass # else: # #print('stop', self.ballVs[ballIndex], self.accelVs[ballIndex]) # #self.ballVs[ballIndex] = LVector3(0, 0, 0) # #self.accelVs[ballIndex] = LVector3(0, 0, 0) # pass # pass return # This is the task that deals with making everything interactive def rollTask(self, task): # Standard technique for finding the amount of time since the last # frame dt = globalClock.getDt() # If dt is large, then there has been a # hiccup that could cause the ball # to leave the field if this functions runs, so ignore the frame if dt > .2: return Task.cont # if base.mouseWatcherNode.is_button_down('a'): # self.holeRoot.setH(self.holeRoot.getH() + 1) # print(self.holeRoot.getHpr()) # pass # if base.mouseWatcherNode.is_button_down('s'): # self.holeRoot.setP(self.holeRoot.getP() + 1) # print(self.holeRoot.getHpr()) # pass # if base.mouseWatcherNode.is_button_down('d'): # self.holeRoot.setR(self.holeRoot.getR() + 1) # print(self.holeRoot.getHpr()) # pass # go through different visualizations if base.mouseWatcherNode.is_button_down( 'space') and self.showing == 'none': self.showing = 'parts' self.showingProgress = 0 pass #print(self.showing) #print(self.showing) if self.showing == 'none': return Task.cont if self.showing == 'parts': self.showingProgress += 0.01 #self.showingProgress += 1 #print(self.showingProgress) scale = 2 - self.showingProgress scaleY = 1 + (scale - 1) * 0.5 for planeIndex, planeNP in enumerate(self.planeNPs): center = self.planeCenters[planeIndex] planeNP.setPos(center[0] * scale, center[1] * scaleY, center[2] * scale) planeNP.reparentTo(self.render) planeNP.setTwoSided(True) continue if self.showingProgress > 1: self.showing = 'moving' for planeIndex, planeNP in enumerate(self.planeNPs): planeNP.removeNode() continue self.planeScene.show() self.showingProgress = 1 return Task.cont if self.showing == 'moving': self.showingProgress += 0.005 #self.showingProgress += 1 #print(self.showingProgress, np.sign(self.showingProgress - 0.5) * min(self.showingProgress % 0.5, 0.5 - self.showingProgress % 0.5) * 4) self.camera.setPos( np.sign(self.showingProgress - 0.5) * min(self.showingProgress % 0.5, 0.5 - self.showingProgress % 0.5) * 3, 0, 0) #self.camera.setHpr(angleDegrees, 0, 0) #self.camera.lookAt(0, 0, 0) self.camera.lookAt(0, 3, 0) if self.showingProgress > 1: self.showing = 'geometry' self.camera.setPos(0, 0, 0) #self.planeScene.removeNode() # for triNP in self.triNPs: # triNP.show() # continue self.showingProgress = 1 return Task.cont if self.showing == 'geometry': self.showingProgress += 0.02 if self.showingProgress > 1: #self.showing = 'image' self.showing = 'placement' self.showingProgress = 0 self.holeRoot.show() self.inPortalRoot.show() self.outPortalRoot.show() self.inPortalTube.show() self.outPortalTube.show() for ballRoot in self.ballRoots: ballRoot.show() continue self.showingProgress = 0 pass return Task.cont # if self.showing == 'placement': # self.showingProgress += 0.005 # continue # mouse pose if self.mouseWatcherNode.hasMouse(): mpos = self.mouseWatcherNode.getMouse() self.mpos = mpos self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY()) pass #if base.mouseWatcherNode.is_button_down('space') and self.showing == 'placement': if self.showing == 'placement': self.card.show() self.planeScene.removeNode() self.showing = 'image' pass # if base.mouseWatcherNode.is_button_down('space') and self.showing == 'image': # for triNP in self.triNPs: # triNP.hide() # continue # self.showing = 'start' # pass # for each plane, check which horizontal plane it is sitting on self.ballGroundMap = {} for i in range(self.cHandler.getNumEntries()): entry = self.cHandler.getEntry(i) ballName = entry.getFromNode().getName() groundName = entry.getIntoNode().getName() if 'ball_ray_' not in ballName: continue if 'ground_' not in groundName: continue ballIndex = int(ballName[9:]) groundIndex = int(groundName[7:]) norm = -entry.getSurfaceNormal(render) if norm.length() == 0: continue norm = norm / norm.length() distance = norm.dot( entry.getSurfacePoint(render) - self.ballRoots[ballIndex].getPos()) #print(distance) if distance < 0: continue if ballIndex not in self.ballGroundMap or distance < self.ballGroundMap[ ballIndex][1]: self.ballGroundMap[ballIndex] = (groundIndex, distance) pass continue # The collision handler collects the collisions. We dispatch which function # to handle the collision based on the name of what was collided into for i in range(self.cHandler.getNumEntries()): entry = self.cHandler.getEntry(i) fromName = entry.getFromNode().getName() #if 'mouseRay' in fromName: #continue name = entry.getIntoNode().getName() #if name == "plane_collide": if 'tri_' in name: self.planeCollideHandler(entry) #elif name == "wall_collide": #self.wallCollideHandler(entry) #elif name == "ground_collide": #self.groundCollideHandler(entry) elif 'ball_' in name: self.ballCollideHandler(entry) elif 'ground_' in name: self.groundCollideHandler(entry) elif 'hole' in name: self.score(entry) elif 'portal_' in name: self.portal(entry) pass continue # Read the mouse position and tilt the maze accordingly if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() # get the mouse position #self.maze.setP(mpos.getY() * -10) #self.maze.setR(mpos.getX() * 10) pass # if base.mouseWatcherNode.is_button_down('mouse1'): # print(base.mouseWatcherNode.getMouseX()) # print(base.mouseWatcherNode.getMouseY()) # exit(1) # Finally, we move the ball # Update the velocity based on acceleration for ballIndex in xrange(len(self.balls)): if self.ballVs[ballIndex].length( ) < 1e-4 and self.ballVs[ballIndex].dot( self.accelVs[ballIndex]) < -1e-4: self.ballVs[ballIndex] = LVector3(0, 0, 0) self.accelVs[ballIndex] = LVector3(0, 0, 0) else: self.ballVs[ballIndex] += self.accelVs[ballIndex] * dt * ACCEL pass #print('current speed', self.ballVs[ballIndex], self.accelVs[ballIndex]) # Clamp the velocity to the maximum speed if self.ballVs[ballIndex].lengthSquared() > MAX_SPEED_SQ: self.ballVs[ballIndex].normalize() self.ballVs[ballIndex] *= MAX_SPEED pass #print(self.ballVs[ballIndex], self.accelVs[ballIndex], self.ballRoots[ballIndex].getPos()) # Update the position based on the velocity self.ballRoots[ballIndex].setPos( self.ballRoots[ballIndex].getPos() + (self.ballVs[ballIndex] * dt)) # This block of code rotates the ball. It uses something called a quaternion # to rotate the ball around an arbitrary axis. That axis perpendicular to # the balls rotation, and the amount has to do with the size of the ball # This is multiplied on the previous rotation to incrimentally turn it. prevRot = LRotationf(self.balls[ballIndex].getQuat()) axis = LVector3.up().cross(self.ballVs[ballIndex]) newRot = LRotationf( axis, np.rad2deg(dt * self.ballVs[ballIndex].length() / self.ballSize)) self.balls[ballIndex].setQuat(prevRot * newRot) continue self.cueRoot.setPos(self.cuePos[0], self.cuePos[1], self.cuePos[2]) return Task.cont # Continue the task indefinitely # handle scoring (when a ball fall in a hole) def score(self, colEntry): ballName = colEntry.getFromNode().getName() if 'ball_' not in ballName: return print('score', ballName) ballIndex = int(ballName[5:]) self.ballRoots[ballIndex].removeNode() del self.ballRoots[ballIndex] del self.balls[ballIndex] del self.ballSpheres[ballIndex] del self.ballGroundRays[ballIndex] del self.ballVs[ballIndex] del self.accelVs[ballIndex] for otherIndex in xrange(ballIndex, len(self.balls)): self.ballSpheres[otherIndex].setName('ball_' + str(otherIndex)) self.ballGroundRays[otherIndex].setName('ball_ray_' + str(otherIndex)) continue return
class GamingCam(object,DirectObject): yshift,zshift=5,5 ymin,ymax=10,70 zmin,zmax=10,70 zoom_speed=.1 move_speed=.5 def __init__(self,gmap,gaming_zone): DirectObject.__init__(self) #gaming zone (used for mouse movement), as a tools.Rectangle self.gaming_zone=gaming_zone #actual camera node self.p3dcam=base.camera #what the cam is oriented to self._target=base.render.attachNewNode('GaminCam.target') #range=[0,1] between min and max closeness to ground self.level=.7 # #keys_down acts as a pool containing keys (+mouse buttons) currently down self.keys_down=[] update_list.append(self.update) #setup for mouse picking picker_node=CollisionNode('gcam_to_mouse_ray')#general collision node picker_node.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.picker_ray=CollisionRay()#solid ray to attach to coll node picker_node.addSolid(self.picker_ray) self.picker_np=self.p3dcam.attachNewNode(picker_node)#attach this node to gcam self.collision_queue=CollisionHandlerQueue()#stores collisions self.collision_traverser=CollisionTraverser('gcam_traverser')#actual computer self.collision_traverser.addCollider(self.picker_np,self.collision_queue) base.cTrav=self.collision_traverser self.gmap=gmap #stack of states (state=pos+zoom) self.states_stack=[] #enable the cam to move according to keyboard and mouse self.move_enabled=True def __del__(self): update_list.remove(self.update) self.ignoreAll() def center(self): self._target.setPos(0,0,0) def disable_move(self): self.move_enabled=False def enable_move(self): self.move_enabled=True def get_level(self): return self._level def get_picked_tile(self): ''' returns ''' if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position # self.picker_ray.setFromLens(self.p3dcam.node().getLens(), mpos.getX(), mpos.getY()) self.picker_ray.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.collision_traverser.traverse(self.gmap.tile_matrix_node) if self.collision_queue.getNumEntries()>0: #useless since collision test is done against a single object self.collision_queue.sortEntries() entry=self.collision_queue.getEntry(0) x,y,_=entry.getSurfacePoint(self.gmap.tile_matrix_node) x=(x+self.gmap.resx)/2. y=(y+self.gmap.resy)/2. x=max(x,0) x=min(x,self.gmap.resx) y=max(y,0) y=min(y,self.gmap.resy) x=int(x) y=int(y) #out(pos=(x,y,z)) return self.gmap.tile_matrix[x][y] return None def get_picked_unit(self): if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position # self.picker_ray.setFromLens(self.p3dcam.node().getLens(), mpos.getX(), mpos.getY()) self.picker_ray.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.collision_traverser.traverse(self.gmap.units_node) if self.collision_queue.getNumEntries()>0: #useless since collision test is done against a single object self.collision_queue.sortEntries() entry=self.collision_queue.getEntry(0) return entry.getIntoNodePath().findNetTag('GUnit-pickable') return None def get_target(self): return self._target def mouse_up(self,btn): if btn=='left': target=self.get_picked_tile() #out(target) #print NodePath(self.gmap.tile_matrix_node).ls() #print render.analyze() if target: network.serverproxy.send({network.cts_dbg_dump_entity:{'eid':target.eid}}) elif btn=='middle': self.set_level(.7) self.set_target(Vec3(0,-9,0)) def move(self,dx=0,dy=0): self._target.setPos(self._target,dx,dy,0) self.update_cam() def push_state(self): out('GCam.push_state()') pos,zoom=self._target.getPos(),self.level self.states_stack.append((pos,zoom)) def pop_state(self): out('GCam.pop_state()') pos,zoom=self.states_stack.pop(-1) self._target.setPos(*pos) self.level=zoom self.update_cam() def set_level(self,level): self._level=level self.update_cam() def set_target(self,target): ''' make the cam look at the given target. target can be a set of coordinates, or a node/nodepath. ''' if isinstance(target,PandaNode) or isinstance(target,NodePath): self._target.setPos(target,0,0,0) if isinstance(target,tuple): self._target.setPos(Vec3(*target)) else: self._target.setPos(target) self.update_cam() def start_accepting(self): ''' the gaming cam is created at gmap creation, so it has to wait until all data structures are created before accepting events. ''' self.accept(ConfigVariableString('key-cam-zoom-in').getValue()+'-up',self.zoom,extraArgs=[-GamingCam.zoom_speed]) self.accept(ConfigVariableString('key-cam-zoom-out').getValue()+'-up',self.zoom,extraArgs=[GamingCam.zoom_speed]) self.accept(ConfigVariableString('key-cam-right').getValue(),self.keys_down.append,extraArgs=['r']) self.accept(ConfigVariableString('key-cam-right').getValue()+'-up',self.keys_down.remove,extraArgs=['r']) self.accept(ConfigVariableString('key-cam-up').getValue(),self.keys_down.append,extraArgs=['u']) self.accept(ConfigVariableString('key-cam-up').getValue()+'-up',self.keys_down.remove,extraArgs=['u']) self.accept(ConfigVariableString('key-cam-left').getValue(),self.keys_down.append,extraArgs=['l']) self.accept(ConfigVariableString('key-cam-left').getValue()+'-up',self.keys_down.remove,extraArgs=['l']) self.accept(ConfigVariableString('key-cam-down').getValue(),self.keys_down.append,extraArgs=['d']) self.accept(ConfigVariableString('key-cam-down').getValue()+'-up',self.keys_down.remove,extraArgs=['d']) self.accept('mouse1-up',self.mouse_up,extraArgs=['left']) self.accept('mouse2-up',self.mouse_up,extraArgs=['middle']) def update(self): dx,dy=0,0 for k in self.keys_down: if k=='r':dx+=GamingCam.move_speed if k=='l':dx-=GamingCam.move_speed if k=='u':dy+=GamingCam.move_speed if k=='d':dy-=GamingCam.move_speed if k=='m': pass #dx+=mouse.getMouseX() #dy+=mouse.getMouseY() if self.move_enabled: self.move(dx,dy) def update_cam(self): self.p3dcam.setPos( self._target, 0, -(GamingCam.ymin+GamingCam.yshift+self._level*GamingCam.ymax), GamingCam.zmin+GamingCam.zshift+self._level*GamingCam.zmax ) self.p3dcam.lookAt(self._target) def zoom(self,delta): #TODO: smoothen the zoom with a task (interpolation) if 0<self._level+delta<1.: self.level+=delta level=property(get_level,set_level) target=property(get_target,set_target)
class LocationSeeker: def __init__(self, avatar, minDistance, maxDistance, shadowScale=1): self.dropShadowPath = 'phase_3/models/props/square_drop_shadow.bam' self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.ogg' self.moveShadowTaskName = 'Move Shadow' self.locationSelectedName = 'Location Selected' self.dropShadow = None self.shadowScale = shadowScale self.rejectSfx = loader.loadSfx(self.rejectSoundPath) self.avatar = avatar self.cameraNode = None self.cameraRay = None self.cameraNP = None self.shadowNP = None self.shadowRay = None self.minDistance = minDistance self.maxDistance = maxDistance self.legacyMode = False self.collHdlFl = CollisionHandlerQueue() self.moveShadowEventName = 'LocationSeeker-MoveShadow' return def startSeeking(self): if not self.avatar: return self.cleanupShadow() self.buildShadow() scale = self.dropShadow.getScale() if scale < 1.0: self.maxDistance += 40 x, y, z = self.avatar.getPos(render) self.dropShadow.reparentTo(render) self.dropShadow.setPos(x, y + 5, z + 2) self.cameraNode = CollisionNode('coll_camera') self.cameraNode.setFromCollideMask(CIGlobals.WallBitmask) self.cameraRay = CollisionRay() self.cameraNode.addSolid(self.cameraRay) self.cameraNP = camera.attachNewNode(self.cameraNode) base.cTrav.addCollider(self.cameraNP, CollisionHandlerQueue()) if not self.legacyMode: shadowNode = CollisionNode('coll_shadow') self.shadowRay = CollisionRay(0, 0, 6, 0, 0, -1) shadowNode.addSolid(self.shadowRay) shadowNode.setFromCollideMask(CIGlobals.FloorBitmask) self.shadowNP = self.dropShadow.attachNewNode(shadowNode) base.cTrav.addCollider(self.shadowNP, self.collHdlFl) base.taskMgr.add(self.__moveShadow, self.moveShadowTaskName) self.avatar.acceptOnce('mouse1', self.locationChosen) def stopSeeking(self): base.taskMgr.remove(self.moveShadowTaskName) def __moveShadow(self, task): if base.mouseWatcherNode.hasMouse(): prevPos = self.dropShadow.getPos(render) def PointAtZ(z, point, vec): if vec.getZ() != 0: return point + vec * ((z - point.getZ()) / vec.getZ()) return self.getLocation() mouse = base.mouseWatcherNode.getMouse() self.cameraRay.setFromLens(base.camNode, mouse.getX(), mouse.getY()) nearPoint = render.getRelativePoint(camera, self.cameraRay.getOrigin()) nearVec = render.getRelativeVector(camera, self.cameraRay.getDirection()) self.dropShadow.setPos(PointAtZ(0.5, nearPoint, nearVec)) if (prevPos - self.dropShadow.getPos(render)).length() >= 0.25: messenger.send(self.moveShadowEventName) if self.legacyMode: self.dropShadow.setZ(base.localAvatar.getZ(render) + 0.5) elif self.collHdlFl.getNumEntries() > 0: self.dropShadow.setZ( self.collHdlFl.getEntry(0).getSurfacePoint(render).getZ() + 0.5) return Task.cont def locationChosen(self): base.taskMgr.remove(self.moveShadowTaskName) distance = self.avatar.getDistance(self.dropShadow) x, y, z = self.getLocation() if distance >= self.minDistance and distance <= self.maxDistance: gag = self.avatar.getBackpack().getActiveGag() self.avatar.sendUpdate('setDropLoc', [gag.getID(), x, y, z]) messenger.send(self.locationSelectedName) else: self.rejectSfx.play() self.avatar.acceptOnce('mouse1', self.locationChosen) base.taskMgr.add(self.__moveShadow, self.moveShadowTaskName) def buildShadow(self): self.cleanupShadow() if not self.dropShadowPath or not self.avatar: return self.dropShadow = loader.loadModel(self.dropShadowPath) self.dropShadow.setScale(self.shadowScale) self.dropShadow.setName('LocationSeeker_Shadow') def setShadowType(self, isCircle=False, scale=1): if not isCircle: self.dropShadowPath = 'phase_3/models/props/square_drop_shadow.bam' else: self.dropShadowPath = 'phase_3/models/props/drop_shadow.bam' self.shadowScale = scale def getDropShadow(self): return self.dropShadow def getLocation(self): if self.dropShadow: return self.dropShadow.getPos(render) return self.avatar.getPos(render) def getLocationSelectedName(self): return self.locationSelectedName def getShadowMovedName(self): return self.moveShadowEventName def cleanupShadow(self): if self.dropShadow: self.dropShadow.removeNode() self.dropShadow = None if self.cameraNode: self.cameraNP.removeNode() self.cameraNP = None self.cameraNode = None self.cameraRay = None self.shadowNP.removeNode() self.shadowRay = None self.shadowNP = None self.shadowSphNP = None return def cleanup(self): if self.avatar: base.taskMgr.remove(self.moveShadowTaskName) self.avatar.ignore('mouse1') self.cleanupShadow() self.rejectSfx.stop() self.rejectSfx = None self.avatar = None self.dropShadowPath = None self.rejectSoundPath = None self.locationSelectedName = None self.moveShadowTaskName = None self.moveShadowEventName = None self.collHdlFl = None del self.collHdlFl del self.minDistance del self.maxDistance del self.legacyMode del self.dropShadow del self.cameraNP del self.cameraNode del self.cameraRay del self.shadowNP del self.shadowRay del self.shadowSphNP del self.shadowScale del self.rejectSfx del self.avatar del self.dropShadowPath del self.rejectSoundPath del self.locationSelectedName del self.moveShadowTaskName del self.moveShadowEventName return
class Battle(object, DirectObject): """ Battle shows UI, includes controller etc. """ def __init__(self, nick): self.player = player.Player(nick=nick) self.controller = controller.ClientController(self.player) self.handgui = hand.HandGUI(self.player.hand) camera.setPos(0, -20, 0) self.accept("turn_time_changed", self.update_turn_time) self.turn_timer = TurnTimer() # Mouse detection: base.cTrav = CollisionTraverser() self.mouse_ray_handler = CollisionHandlerQueue() mouse_ray_node = CollisionNode('mouse_ray') mouse_ray_np = camera.attachNewNode(mouse_ray_node) mouse_ray_node.setFromCollideMask(GeomNode.getDefaultCollideMask()) mouse_ray_node.setIntoCollideMask(0) self.mouse_ray = CollisionRay() mouse_ray_node.addSolid(self.mouse_ray) base.cTrav.addCollider(mouse_ray_np, self.mouse_ray_handler) self.mouse_overed_entry = None self.accept("mouse_enter", self.enlarge_entry) self.accept("mouse_leave", self.shrink_entry) self.accept("p-up", self.change_card_picture) taskMgr.doMethodLater(0.1, self.check_mouse_over, "check_mouse_over") def update_turn_time(self, turn_time): self.turn_timer.time_left = turn_time self.turn_timer.update_displayed_time() def check_mouse_over(self, task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.mouse_ray.setFromLens(base.camNode, mpos.getX(), mpos.getY()) base.cTrav.traverse(render) entries_amount = self.mouse_ray_handler.getNumEntries() if entries_amount > 0: self.mouse_ray_handler.sortEntries() entry = self.mouse_ray_handler.getEntry(0) if entry != self.mouse_overed_entry: messenger.send('mouse_leave', [self.mouse_overed_entry]) messenger.send('mouse_enter', [entry]) self.mouse_overed_entry = entry self.mouse_over(entry) else: if self.mouse_overed_entry: # TODO: Make sure this does not cause referencing errors messenger.send('mouse_leave', [self.mouse_overed_entry]) self.mouse_overed_entry = None return task.again def mouse_over(self, entry): object_np = entry.getIntoNodePath() objekt = object_np.findNetTag("object_type") # object is reserved word if objekt.isEmpty(): print "No object type detected" return object_type = objekt.getTag("object_type") if object_type == "card": self.handgui.mouse_over(objekt.getPythonTag('owner')) def enlarge_entry(self, entry): entry_np = entry.getIntoNodePath() card_root = None if entry_np.hasPythonTag('root'): card_root = entry_np.getPythonTag('root') # TODO: Replace magic number with variable card_root.setY(self.handgui.nodepath, -15) def shrink_entry(self, entry): if not entry: return entry_np = entry.getIntoNodePath() card_root = None if entry_np.hasPythonTag('root'): card_root = entry_np.getPythonTag('root') card_root.setY(self.handgui.nodepath, 0) def change_card_picture(self): self.handgui.hand[0].change_picture()
class CameraManager(): ZOOM_SPEED = 50 CAM_X = 'camX' CAM_Y = 'camY' CAM_Z = 'camZ' def __init__(self, showBase, camDict): self.showBase = showBase self.showBase.disableMouse() self.setSettings(camDict) self.initMouseToWorldCoordConversion() self.initMouseRayCollision() self.savedCollisionPoint = Vec3(0, 0, 0) # End """ init helpers """ def setSettings(self, camDict): cam = self.showBase.camera cam.setPos(50, 0, -1000) self.camPos = cam.getPos() cam.setHpr(0, 90, 0) self.showBase.camLens.setFov(10) self.dragging = False self.loadSettings(camDict) def initMouseRayCollision(self): z = 0 self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, z)) def initMouseToWorldCoordConversion(self): self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = self.showBase.camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) """ Events """ def zoomIn(self): camera = self.showBase.camera curPos = camera.getPos() curPos.z += CameraManager.ZOOM_SPEED camera.setPos(curPos) self.saveSettings(self.camDict) def zoomOut(self): camera = self.showBase.camera curPos = camera.getPos() curPos.z += -CameraManager.ZOOM_SPEED camera.setPos(curPos) self.saveSettings(self.camDict) def mouse1Down(self): self.savedCollisionPoint = self.getMouseCollisionToPlane(self.plane) # print("mouse1Down") def mouseMove(self, task): collisionPoint = self.getMouseCollisionToPlane(self.plane) delta = self.getDelta(collisionPoint, self.savedCollisionPoint) self.addToCameraPos(delta) # Collision point changes if camera position changes collisionPoint = self.getMouseCollisionToPlane(self.plane) self.savedCollisionPoint = collisionPoint return Task.cont """ mouse1Down and mouseMove helpers """ def getMouseCollisionToPlane(self, plane): mouseWatcherNode = self.showBase.mouseWatcherNode if mouseWatcherNode.hasMouse(): mpos = mouseWatcherNode.getMouse() pos3d = Point3() nearPoint = Point3() farPoint = Point3() self.showBase.camLens.extrude(mpos, nearPoint, farPoint) render = self.showBase.render camera = self.showBase.camera if plane.intersectsLine(pos3d, render.getRelativePoint(camera, nearPoint), render.getRelativePoint(camera, farPoint)): return pos3d return None """ mouseMove helpers """ def getDelta(self, point1, point2): delta = Vec3() if point1 is not None: if point1.almostEqual(point2) is False: delta = point2 - point1 return delta def addToCameraPos(self, delta): camera = self.showBase.camera curPos = camera.getPos() camera.setPos(curPos + delta) self.saveSettings(self.camDict) def setViewBasedOnNodePos(self, pos): camera = self.showBase.camera newPos = Vec3(camera.getPos()) newPos.x = pos.x newPos.y = pos.y camera.setPos(newPos) # NodePath datection is manage internally in Panda3D, NodeManager should have been # managing NodePath, but it can be handled by communication with Camera and # Panda3D already, so NodeManager is not needed anymore here # TODO: Refactor def getClickedNodePath(self): mouseWatcherNode = self.showBase.mouseWatcherNode if mouseWatcherNode.hasMouse(): mpos = mouseWatcherNode.getMouse() self.pickerRay.setFromLens(self.showBase.camNode, mpos.getX(), mpos.getY()) self.picker.traverse(self.showBase.render) if self.pq.getNumEntries() > 0: self.pq.sortEntries() return self.pq.getEntry(0).getIntoNodePath() return None def getCoordinates(self): mouseWatcherNode = self.showBase.mouseWatcherNode mpos = mouseWatcherNode.getMouse() self.pickerRay.setFromLens(self.showBase.camNode, mpos.getX(), mpos.getY()) render = self.showBase.render camera = self.showBase.camera nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) return mpos, nearPoint def saveSettings(self, camDict): self.camDict = camDict cam = self.showBase.camera pos = cam.getPos() camDict[CameraManager.CAM_X] = pos.x camDict[CameraManager.CAM_Y] = pos.y camDict[CameraManager.CAM_Z] = pos.z def loadSettings(self, camDict): self.camDict = camDict if camDict is None: self.camDict = {} if camDict.get(CameraManager.CAM_X) is not None: camera = self.showBase.camera camera.setPos(camDict[CameraManager.CAM_X], camDict[CameraManager.CAM_Y], camDict[CameraManager.CAM_Z]) def showValues(self): print("camera pos " + str(self.showBase.camera.getPos())) print("camera hpr " + str(self.showBase.camera.getHpr())) print("camera x " + str(self.showBase.camera.getX())) print("camera y " + str(self.showBase.camera.getY())) print("camera z " + str(self.showBase.camera.getZ()))
class p3dApp(ShowBase): def __init__(self): ShowBase.__init__(self); # setup the environment or model self.model = \ self.loader.loadModel("/usr/share/panda3d/models/box"); self.model.reparentTo(self.render); self.model.setTag('Model', '1'); self.model.setScale(1.5, 1.5, 1.5); # setup camera self.camera.setPos(5,5,5) self.camera.lookAt(0,0,0) # Disable mouse control self.disableMouse(); # Handle mouse events. self.accept('mouse1', self.mouse_down); # convert image from opencv to panda3d texture # self.taskMgr.add(self.read_image_cv, "cvImageTask"); # Setup collision handler self.handler = CollisionHandlerQueue() self.traverser = CollisionTraverser('ColTraverser') self.traverser.traverse(self.model) self.ray = CollisionRay() pickerNode = CollisionNode('MouseRay') pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) pickerNP = self.camera.attachNewNode(pickerNode) pickerNode.addSolid(self.ray) self.traverser.addCollider(pickerNP, self.handler) self.load_shader(); self.first_frame_loaded = False; def read_image_cv(self): """ Pulls the next frame from the opencv part, and converts to a panda3d texture and display it on the screen. """ cvim = self.cvapp.pull_frame() w = cvim.shape[1]; h = cvim.shape[0]; cvim = cv2.flip(cvim, 0); self.im = Texture("cvIm"); self.im.setCompression(Texture.CMOff); self.im.setup2dTexture(w, h, Texture.TUnsignedByte, Texture.FLuminance); self.im.setRamImage(cvim); self.screen_im = OnscreenImage(parent=self.render2d, image=self.im, scale=(1, 1, 1), pos=(0, 0, 0)); self.cam2d.node().getDisplayRegion(0).setSort(-20); def load_shader(self): """ The function loads the vertex and fragment shader. It provides an example of sending the model-view-projection matrix to the shader program when it's calculated. """ self.shader = Shader.load(Shader.SL_GLSL, "vertex.glsl", "fragment.glsl"); self.model.set_shader(self.shader) self.model.set_shader_input("my_ModelViewProjectionMatrix", LMatrix4f()) def mouse_down(self): """ This function is called as a result of a mouse click. It gets the vertex that was clicked by the mouse. It sends the mouse position and the vertex position to the cv app. """ if (self.first_frame_loaded == False): self.first_frame_loaded = True self.read_image_cv() return; xPos = self.mouseWatcherNode.getMouseX() yPos = self.mouseWatcherNode.getMouseY() self.ray.setFromLens(self.camNode, xPos, yPos) self.traverser.traverse(self.model) self.handler.sortEntries() if (self.handler.getNumEntries() > 0): entry = self.handler.getEntry(0) # CollisionEntry vpos = entry.getSurfacePoint(self.model) res = self.cvapp.mouse_clicked(LPoint3f(xPos, yPos), vpos) if (res == 1): self.read_image_cv() def set_cv_app(self, cvapp): self.cvapp = cvapp;
class ChessboardDemo(ShowBase): def __init__(self, client): ShowBase.__init__(self) self.client = client # This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking", style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1), pos=(0.8, -0.95), scale = .07) self.escapeEvent = OnscreenText( text="ESC: Quit", parent=base.a2dTopLeft, style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.1), align=TextNode.ALeft, scale = .05) self.mouse1Event = OnscreenText( text="Left-click and drag: Pick up and drag piece", parent=base.a2dTopLeft, align=TextNode.ALeft, style=1, fg=(1, 1, 1, 1), pos=(0.06, -0.16), scale=.05) self.accept('escape', sys.exit) # Escape quits self.disableMouse() # Disable mouse camera control camera.setPosHpr(0, -12, 8, 0, -35, 0) # Set the camera self.setupLights() # Setup default lighting # Since we are using collision detection to do picking, we set it up # like any other collision detection system with a traverser and a # handler self.picker = CollisionTraverser() # Make a traverser self.pq = CollisionHandlerQueue() # Make a handler # Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be # positioned relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing # other collisions we could separate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() # Make our ray # Add it to the collision node self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) # self.picker.showCollisions(render) # Now we create the chess board and its pieces # We will attach all of the squares to their own root. This way we can # do the collision pass just on the squares and save the time of # checking the rest of the scene self.square_root = render.attachNewNode("square_root") # For each square self.squares = [None for i in range(64)] for i in range(64): # Load, parent, color, and position the model (a single square # polygon) self.squares[i] = loader.loadModel("models/square") self.squares[i].reparentTo(self.square_root) self.squares[i].setPos(square_pos(i)) self.squares[i].setColor(square_color(i)) # Set the model itself to be collidable with the ray. If this model # is any more complex than a single polygon, you should set up a # collision sphere around it instead. But for single polygons this # works fine. self.squares[i].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) # Set a tag on the square's node so we can look up what square it is # later during the collision pass self.squares[i].find("**/polygon").node().setTag('square', str(i)) # We will use this variable as a pointer to whatever piece is # currently in this square # This will represent the index of the currently highlighted square self.hiSq = False # Represents the index of the square where currently dragged piece # was grabbed from self.dragging = False # Start the task that handles the picking self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') self.accept("mouse1", self.grab_piece) # left-click grabs a piece self.accept("mouse1-up", self.release_piece) # releasing places it def swap_pieces(self, fr, to): # Get the objects from the square references fr_model, fr_do = self.model_at(fr), self.piece_at(fr) to_model, to_do = self.model_at(to), self.piece_at(to) # Handle the from model fr_model.setPos(square_pos(to)) fr_do.square = to self.client._save(fr_do) # Handle the to model if it exists if to_do: to_model.setPos(square_pos(fr)) to_do.square = fr self.client._save(to_do) def mouseTask(self, task): # This task deals with the highlighting and dragging based on the mouse # First, clear the current highlight if self.hiSq is not False: self.squares[self.hiSq].setColor(square_color(self.hiSq)) self.hiSq = False # Check to see if we can access the mouse. We need it to do anything # else if self.mouseWatcherNode.hasMouse(): # get the mouse position mouse_pos = self.mouseWatcherNode.getMouse() # Set the position of the ray based on the mouse position self.pickerRay.setFromLens( self.camNode, mouse_pos.getX(), mouse_pos.getY()) # If we are dragging something, set the position of the object # to be at the appropriate point over the plane of the board if self.dragging is not False: # Gets the point described by pickerRay.getOrigin(), which is # relative to camera, relative instead to render nearPoint = render.getRelativePoint( camera, self.pickerRay.getOrigin()) # Same thing with the direction of the ray nearVec = render.getRelativeVector( camera, self.pickerRay.getDirection()) model = self.model_at(self.dragging) model.setPos( point_at_z(.5, nearPoint, nearVec)) # Do the actual collision pass (Do it only on the squares for # efficiency purposes) # noinspection PyArgumentList self.picker.traverse(self.square_root) if self.pq.getNumEntries() > 0: # if we have hit something, sort the hits so that the closest # is first, and highlight that node self.pq.sortEntries() i = int(self.pq.getEntry(0).getIntoNode().getTag('square')) # Set the highlight on the picked square self.squares[i].setColor(HIGHLIGHT) self.hiSq = i return Task.cont def piece_at(self, square): for p in self.client.objects: if p.square == square: return p return None def model_at(self, square): for p in self.client.objects: if p.square == square: return self.client.models[p.id] return None def grab_piece(self): # If a square is highlighted and it has a piece, set it to dragging # mode if self.hiSq is not False and self.piece_at(self.hiSq): self.dragging = self.hiSq self.hiSq = False def release_piece(self): # If we are not on a square, return it to its original position. # Otherwise, swap it with the piece in the new square # Make sure we really are dragging something if self.dragging is not False: # We have let go of the piece, but we are not on a square if self.hiSq is False: model = self.model_at(self.dragging) model.setPos( square_pos(self.dragging)) else: # Otherwise, swap the pieces self.swap_pieces(self.dragging, self.hiSq) # We are no longer dragging anything self.dragging = False def setupLights(self): # This function sets up some default lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.8, .8, .8, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(LVector3(0, 45, -45)) directionalLight.setColor((0.2, 0.2, 0.2, 1)) render.setLight(render.attachNewNode(directionalLight)) render.setLight(render.attachNewNode(ambientLight))
class Picker(object): """ Generic object picker class for Panda3d. Given a top Node Path to search, it finds the closest collision object under the mouse pointer. Picker takes a topNode to test for mouse ray collisions. the pick() method returns (NodePathPicked, 3dPosition, rawNode) underneath the mouse position. If no collision was detected, it returns None, None, None. 'NodePathPicked' is the deepest NAMED node path that was collided with, this is usually what you want. rawNode is the deep node (such as geom) if you want to play with that. 3dPosition is where the mouse ray touched the surface. The picker object uses base.camera to collide, so if you have a custom camera, well, sorry bout that. pseudo code: p = Picker(mycollisionTopNode) thingPicked, positionPicked, rawNode = p.pick() if thingPicked: # do something here like thingPicked.ls() """ def __init__(self, topNode, cameraObject = None): self.traverser = CollisionTraverser() self.handler = CollisionHandlerQueue() self.topNode = topNode self.cam = cameraObject pickerNode = CollisionNode('MouseRay') #NEEDS to be set to global camera. boo hoo self.pickerNP = base.camera.attachNewNode(pickerNode) # this seems to enter the bowels of the node graph, making it # difficult to perform logic on pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.pickRay = CollisionRay() #pickerNode.setCollideMask(BitMask32.allOff()) pickerNode.addSolid(self.pickRay) self.traverser.addCollider(self.pickerNP, self.handler) def setTopNode(self, topNode): """set the topmost node to traverse when detecting collisions""" self.topNode = topNode def destroy(self): """clean up my stuff.""" self.ignoreAll() # remove colliders, subnodes and such self.pickerNP.remove() self.traverser.clearColliders() def pick(self): """ pick closest object under the mouse if available. returns ( NodePathPicked, surfacePoint, rawNode ) or (None, None None) """ if not self.topNode: return None, None, None if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.traverser.traverse(self.topNode) if self.handler.getNumEntries() > 0: self.handler.sortEntries() picked = self.handler.getEntry(0).getIntoNodePath() thepoint = self.handler.getEntry(0).getSurfacePoint(self.topNode) return self.getFirstParentWithName(picked), thepoint, picked return None, None, None def getFirstParentWithName(self, pickedObject): """ return first named object up the node chain from the picked node. This helps remove drudgery when you just want to find a simple object to work with. Normally, you wouldn't use this method directly. """ name = pickedObject.getName() parent = pickedObject while not name: parent = parent.getParent() if not parent: raise Exception("Node '%s' needs a parent with a name to accept clicks." % (str(pickedObject))) name = parent.getName() if parent == self.topNode: raise Exception("Collision parent '%s' is top Node, surely you wanted to click something beneath it..." % (str(parent))) return parent
class HasSelectables(HasKeybinds): #mixin see chessboard example def __init__(self): #selection detection self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = camera.attachNewNode(self.pickerNode) #self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) #TODO WOW geometry collision is SUPER slow... self.pickerNode.setFromCollideMask(BitMask32.bit(BITMASK_COLL_CLICK)) #render.find('**selectable').node().setIntoCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) #box selection detection HINT: start with drawing the 2d thing yo! self.__shift__ = False self.accept("shift", self.shiftOn) self.accept("shift-up", self.shiftOff) self.__ctrl__ = False self.accept("control", self.ctrlOn) self.accept("control-up", self.ctrlOff) #mouse handling self.accept("mouse1", self.clickHandler) self.accept("shift-mouse1", self.clickHandler) self.accept("mouse1-up", self.releaseHandler) #dragging self.dragTask = taskMgr.add(self.dragTask, 'dragTask') def getClickTarget(self,rootSelNode=None): """ traverse from the root of the selectable tree """ #print('getting target....') if rootSelNode == None: rootSelNode = render if base.mouseWatcherNode.hasMouse(): self.pickerRay.setFromLens(base.camNode, *base.mouseWatcherNode.getMouse()) self.picker.traverse(rootSelNode) if self.pq.getNumEntries() > 0: #if we got something sort it self.pq.sortEntries() return self.pq.getEntry(0) #nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) #nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection()) #thingToDrag.obj.setPos(PointAtZ(.5, nearPoint, nearVec)) #not sure how this works def ctrlOn(self): self.__ctrl__ = True def ctrlOff(self): self.__ctrl__ = False def shiftOn(self): self.__shift__ = True def shiftOff(self): self.__shift__ = False @event_callback def clickHandler(self): pass @event_callback def releaseHandler(self): pass def dragTask(self, task): pass def clickSelectObject(self): #shif to multiselect... ?? to unselect invidiual?? pass def dragSelectObjects(self): #always drag in the plane of the camera pass
class World(DirectObject): def __init__(self): self.winprops=WindowProperties() self.winprops.setCursorFilename(Filename.binaryFilename("question-icon.ico")) base.win.setClearColorActive(True) base.win.setClearColor(VBase4(0, 0, 0, 1)) base.win.requestProperties(self.winprops) self.enemyLights = [] self.cameraPositions = [((0, 95, 75), (180, -27, 0)),((0, 55, 25), (180, -15, 0)),((0, -55, 25), (0, -15, 0))] self.cameraIndex = 0 base.disableMouse() base.enableParticles() self.setupLights() self.setupPicking() #Prepare the vehicular manslaughter! self.boosterLightNP = None self.flameLights = None self.player = Vehicle("ralph_models/vampire_car", "ralph_models/vampire_car", self, "player") self.finalGas = None self.livesFrame = DirectFrame(frameColor=(0, 0, 0, 0), parent = base.a2dTopLeft) self.livesSprites = list() for i in range(0,self.player.health): sprite = OnscreenImage(image = 'images/healthicon.png', parent = self.livesFrame, scale = 0.08, pos = (0.2*i+0.1,0,-0.1)) sprite.setTransparency(TransparencyAttrib.MAlpha) self.livesSprites.append(sprite) self.progressFrame = DirectFrame(frameColor=(0, 0, 0, 0), parent = base.a2dpTopRight) gasIcon = OnscreenImage(image = 'images/gas_icon.png', parent = self.progressFrame, scale = 0.04, pos = (-1,0,-0.05)) # gasIcon.reparentTo(aspect2d) gasIcon.setTransparency(TransparencyAttrib.MAlpha) # gasBar = OnscreenImage(image = 'images/gas_bar.png', parent = self.progressFrame, scale = 0.4)#, pos = (-0.9,0,-0.05)) self.gasMax = DirectFrame(frameColor=(.133, .149, .149, 1),frameSize=(-1, 1, -1, 1), parent = self.progressFrame, scale = (0.432,1,0.055625), pos = (-.5,0,-0.04)) self.gasLevel = DirectFrame(frameColor=(.433, .149, .149, 1),frameSize=(-1, -1, -1, 1), parent = self.progressFrame, scale = (0.432,1,0.055625), pos = (-.5,0,-0.04)) gasBar = OnscreenImage(image = 'images/gas_bar_border.png', scale = (1,1,.9), pos = (-.0005,0,-0.04)) gasBar.reparentTo(self.gasLevel) gasBar.setTransparency(TransparencyAttrib.MAlpha) timeBar = OnscreenImage(image = 'images/time_bar.png', parent = self.progressFrame, scale = (0.44,1,0.0525), pos = (-.47,0,-0.15)) self.timePointer = OnscreenImage(image = 'images/time_bar_marker.png', parent = timeBar, scale = (0.05, 0, .2222), pos = (-.83,0,-0.15)) # self.timePointer = OnscreenImage(image = 'images/time_bar_marker.png', parent = self.timeBar, scale = (0.44,1,0.0525), pos = (-.47,0,-0.2)) timeBar.setTransparency(TransparencyAttrib.MAlpha) taskMgr.add(self.updateGasBar, "Update gas") self.loadModels() self.player.setPos(0,0,0) self.setupIntervals() camera.reparentTo(self.player) camera.setPos(self.cameraPositions[0][0][0],self.cameraPositions[0][0][1],self.cameraPositions[0][0][2]) camera.setHpr(self.cameraPositions[0][1][0],self.cameraPositions[0][1][1],self.cameraPositions[0][1][2]) self.setupCollisions() render.setShaderAuto() #you probably want to use this self.keyMap = {"left":0, "right":0, "forward":0, "backwards":0} taskMgr.add(self.player.move, "moveTask") #Give the vehicle direct access to the keyMap self.player.addKeyMap(self.keyMap) #Player Death taskMgr.add(self.deathChecker, "deathTask") #Sounds! self.loadSounds() self.currIcon = "" self.prevtime = 0 self.isMoving = False self.accept("escape", sys.exit) self.accept("arrow_up", self.setKey, ["forward", 1]) self.accept("w", self.setKey, ["forward", 1]) self.accept("arrow_right", self.setKey, ["right", 1]) self.accept("d", self.setKey, ["right", 1]) self.accept("arrow_left", self.setKey, ["left", 1]) self.accept("a", self.setKey, ["left", 1]) self.accept("arrow_down", self.setKey, ["backwards", 1]) self.accept("s", self.setKey, ["backwards", 1]) self.accept("arrow_up-up", self.setKey, ["forward", 0]) self.accept("w-up", self.setKey, ["forward", 0]) self.accept("arrow_right-up", self.setKey, ["right", 0]) self.accept("d-up", self.setKey, ["right", 0]) self.accept("arrow_left-up", self.setKey, ["left", 0]) self.accept("a-up", self.setKey, ["left", 0]) self.accept("arrow_down-up", self.setKey, ["backwards", 0]) self.accept("s-up", self.setKey, ["backwards", 0]) self.accept("mouse1", self.startShoot) self.accept("mouse1-up", self.stopShoot) self.accept("mouse3", self.startDrain ) self.accept("mouse3-up" , self.stopDrain) self.accept("tab", self.shiftCamera) self.accept("space", self.player.startBoosters) self.accept("ate-smiley", self.eat) self.p1 = ParticleEffect() self.p2 = ParticleEffect() self.alan_var = False #Show collisiony stuff if DEBUG: base.cTrav.showCollisions(render) #f = open('testLog.txt', 'r+') #self.dfs(file = f) self.gasPlaying = False self.setLights() self.draining = False taskMgr.add(self.drain, 'drain') self.drainTime = 0.0 self.flamethrowerActive = False self.gasLossTime = 0.0 self.gasLossRate = 1.0 taskMgr.add(self.loseHealth, "loseGas") #After all the loading, we need to calculate our start time self.startTime = datetime.datetime.now() self.timeLimit = datetime.timedelta(seconds=175) timeInterval = LerpPosInterval(self.timePointer, self.timeLimit.seconds, (.8,0,-0.2)) timeInterval.start() def setupPicking(self): self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) self.staticRoot = render.attachNewNode('staticRoot') self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') def dfs(self, item = render, depth = 0, file = None): if file: file.write(("-" * depth) + item.getName() + ": \n") # print(("-" * depth) + item.getName() + ": ") for i in range(item.getNumNodes()): if file: file.write((" " * depth) + "+" + item.getNode(i).getName() + ": " + str(item.getNode(i).getClassType()) + "\n") # print((" " * depth) + "+" + item.getNode(i).getName() + ": " + str(item.getNode(i).getClassType())) for i in range(item.getNumChildren()): self.dfs(item.getChild(i), depth + 1, file) def startDrain(self): if not self.flamethrowerActive: prevDraining = self.draining #previous value of draining if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.picker.traverse(self.staticRoot) if self.pq.getNumEntries() > 0: self.pq.sortEntries() for i in range(self.pq.getNumEntries()): if self.pq.getEntry(i).getIntoNode().getTag('car') != "": self.target = int(self.pq.getEntry(i).getIntoNode().getTag('car')) self.draining = True #Start sounds if self.draining started if self.draining and not prevDraining: self.drainSound.play() def drain(self, task): if self.draining and task.time - self.drainTime > DRAIN_DELAY: carpos = self.staticCars[self.target].getPos() playerpos = self.player.getPos() dist = math.sqrt( (carpos[0] - playerpos[0])**2 + (carpos[1] - playerpos[1])**2 + (carpos[2] - playerpos[2])**2 ) if self.gasList[self.target] > 0 and dist < DRAIN_DIST: if not self.gasPlaying: self.gasP.reset() self.gasP = ParticleEffect() self.gasP.loadConfig(Filename('oil.ptf')) self.gasP.start(self.player) self.gasNode.lookAt(self.staticCars[self.target]) self.gasP.setPos(0,0,2) self.gasP.setScale(1.5) self.gasP.setLightOff() self.gasPlaying = True self.alan_var = False self.gasNode.lookAt(self.staticCars[self.target]) self.gasP.setHpr(self.gasNode.getH() + 180, 90, 0) self.player.totalGas = self.player.totalGas + 1 self.gasList[self.target] = self.gasList[self.target] - 1 else: self.alan_var = True # print "TotalGas: " + str(self.player.totalGas) self.drainTime = task.time elif not self.draining or self.alan_var: self.gasP.softStop() self.drainSound.stop() self.gasPlaying = False return Task.cont def stopDrain(self): self.draining = False def loseHealth(self, task): if task.time - self.gasLossTime > GAS_TIME: if self.player.direction != 0: self.player.totalGas = self.player.totalGas - self.gasLossRate elif self.flamethrowerActive: self.player.totalGas = self.player.totalGas - self.gasLossRate self.gasLossTime = task.time # print self.player.totalGas return Task.cont def mouseTask(self, task): j = -1 if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.picker.traverse(self.staticRoot) if self.pq.getNumEntries() > 0: self.pq.sortEntries() for i in range(self.pq.getNumEntries()): if self.pq.getEntry(i).getIntoNode().getTag('car') != "": j = int(self.pq.getEntry(i).getIntoNode().getTag('car')) carpos = self.staticCars[j].getPos() playerpos = self.player.getPos() dist = math.sqrt( (carpos[0] - playerpos[0])**2 + (carpos[1] - playerpos[1])**2 + (carpos[2] - playerpos[2])**2 ) if self.gasList[j] > 0 and dist < DRAIN_DIST: self.winprops.setCursorFilename(Filename.binaryFilename("vamp-icon.ico")) base.win.requestProperties(self.winprops) elif self.gasList[j] > 0: self.winprops.setCursorFilename(Filename.binaryFilename("vamp-off.ico")) base.win.requestProperties(self.winprops) else: self.winprops.setCursorFilename(Filename.binaryFilename("empty-icon.ico")) base.win.requestProperties(self.winprops) break if j == -1: self.winprops.setCursorFilename(Filename.binaryFilename("question-icon.ico")) base.win.requestProperties(self.winprops) #print j return Task.cont def setupIntervals(self): self.lightOn = LerpFunc(self.lightModify, fromData=0, toData=100, duration=0.2, blendType='noBlend', extraArgs=[True], name="LightUp") self.lightOff = LerpFunc(self.lightModify, fromData=0, toData=100, duration=0.2, blendType='noBlend', extraArgs=[False], name="LightDown") self.cameraMove = None def setKey(self, key, value): self.keyMap[key] = value def setWorldLight(self, object): object.setLight(self.keyLightNP) object.setLight(self.fillLightNP) object.setLight(self.boosterLightNP) for light in self.enemyLights: object.setLight(light) def setLights(self): self.setWorldLight(self.player) self.setWorldLight(self.env) for enemy in self.enemies: self.setWorldLight(enemy) for car in self.staticCars: self.setWorldLight(car) def shiftCamera(self): if self.cameraMove: self.cameraMove.finish() old = self.cameraIndex self.cameraIndex += 1 if self.cameraIndex == len(self.cameraPositions): self.cameraIndex = 0 self.cameraMove=LerpPosHprInterval(camera, .7, self.cameraPositions[self.cameraIndex][0], self.cameraPositions[self.cameraIndex][1], camera.getPos(), camera.getHpr()) self.cameraMove.start() def loadModels(self): self.player.setupBooster() self.env = loader.loadModel("ralph_models/final_terrain") self.env.reparentTo(render) self.env.setScale(8) # Gas particles self.gasP = ParticleEffect() self.gasNode = self.player.attachNewNode('gasNode') # Node Map map = Node.NodeMap("nodes.txt") # enemies self.enemies = [] file = open('levels/enemies.txt' ) line = file.readline().rstrip() self.staticCars = [] self.gasList = [] for currCar in carLocations.cars: target = loader.loadModel("ralph_models/" + currCar['color'] + "_car") target.setPos(currCar['position']) target.setHpr(currCar['direction']) target.reparentTo(self.staticRoot) self.staticCars.append(target) self.gasList.append(currCar['gas']) while line != "" : nums = line.split(',') convertedNums = [] for i in range(len(nums)): if i != 0: convertedNums.append(int(nums[i])) nodePos = map.nodeList[int(nums[0])].getPos() newEnemy = Enemy.Enemy(map, convertedNums, self, nodePos[0], nodePos[1], nodePos[2] ) self.enemies.append( newEnemy ) taskMgr.add(newEnemy.move, "Enemy Move " + str(i), extraArgs = [map], appendTask = True) line = file.readline().rstrip() i = i + 1 def loadSounds(self): self.flamethrowerSound = base.loader.loadSfx("sound/dragonflameloop2.wav") self.flamethrowerEndSound = base.loader.loadSfx("sound/dragonflameend.wav") self.collideSound = base.loader.loadSfx("sound/collide.wav") self.drainSound = base.loader.loadSfx("sound/gas_pump.wav") self.drainSound.setLoop(True) def setupLights(self): #ambient light self.ambientLight = AmbientLight("ambientLight") #four values, RGBA (alpha is largely irrelevent), value range is 0:1 self.ambientLight.setColor((.30, .30, .30, 1)) self.ambientLightNP = render.attachNewNode(self.ambientLight) #the nodepath that calls setLight is what gets illuminated by the light render.setLight(self.ambientLightNP) #call clearLight() to turn it off self.keyLight = DirectionalLight("keyLight") self.keyLight.setColor((.50,.50,.50, 1)) self.keyLightNP = render.attachNewNode(self.keyLight) self.keyLightNP.setHpr(0, -26, 0) self.fillLight = DirectionalLight("fillLight") self.fillLight.setColor((.05,.05,.05, 1)) self.fillLightNP = render.attachNewNode(self.fillLight) self.fillLightNP.setHpr(30, 0, 0) def setupCollisions(self): base.cTrav = CollisionTraverser() self.playerRay = CollisionRay() self.playerRay.setOrigin(0,0,1000) self.playerRay.setDirection(0,0,-1) self.playerNode = CollisionNode("playerRay") self.playerNode.addSolid(self.playerRay) self.playerNode.setFromCollideMask(BitMask32.bit(0)) self.playerNode.setIntoCollideMask(BitMask32.allOff()) self.playerNodePath = self.player.attachNewNode(self.playerNode) self.playerNodePath.show() self.playerGroundHandler = CollisionHandlerQueue() base.cTrav.addCollider(self.playerNodePath, self.playerGroundHandler) envcNode1 = CollisionNode("lot_bottom") envcNode1.setFromCollideMask(BitMask32.bit(0)) temp = CollisionPolygon(Point3(12.56, 19.182, 0), Point3(12.56, -21.261, 0), Point3(-13.217, -21.261, 0), Point3(-13.217, 19.182, 0)) envcNode1.addSolid(temp) envcNode2 = CollisionNode("lot_ramp_bottom") envcNode2.setFromCollideMask(BitMask32.bit(0)) temp = CollisionPolygon(Point3(32.715, -14.923, 3.5), Point3(32.715, -21.261, 3.5), Point3(12.56, -21.261, 0), Point3(12.56, -14.923, 0)) envcNode2.addSolid(temp) envcNode3 = CollisionNode("lot_middle") envcNode3.setFromCollideMask(BitMask32.bit(0)) temp = CollisionPolygon(Point3(42.715, -14.923, 3.5), Point3(42.715, -21.261, 3.5), Point3(32.715, -21.261, 3.5), Point3(32.715, -14.923, 3.5)) envcNode3.addSolid(temp) envcNode4 = CollisionNode("lot_ramp_top") envcNode4.setFromCollideMask(BitMask32.bit(0)) temp = CollisionPolygon(Point3(42.715, -8.845, 6), Point3(42.715, -14.923, 3.5), Point3(32.715, -14.923, 3.5), Point3(32.715, -8.845, 6)) envcNode4.addSolid(temp) envcNode5 = CollisionNode("lot_top") envcNode5.setFromCollideMask(BitMask32.bit(0)) temp = CollisionPolygon(Point3(42.715, 16.155, 6), Point3(42.715, -8.845, 6), Point3(17.715, -8.845, 6), Point3(17.715, 16.155, 6)) envcNode5.addSolid(temp) wallCNode = CollisionNode("fence") wallCNode.setFromCollideMask(BitMask32.bit(0)) temp = CollisionPolygon(Point3(12.56, 19.182, 0), Point3(12.56, -14.923, 0), Point3(12.56, -14.923, 10), Point3(12.56, 19.182, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(12.56, -14.923, 0), Point3(32.715, -14.923, 3.5), Point3(32.715, -14.923, 10), Point3(12.56, -14.923, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(32.715, -14.923, 3.5), Point3(32.715, -8.845, 6), Point3(32.715, -8.845, 10), Point3(32.715, -14.923, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(32.715, -8.845, 6), Point3(17.715, -8.845, 6), Point3(17.715, -8.845, 10), Point3(32.715, -8.845, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(17.715, -8.845, 6), Point3(17.715, 16.155, 6), Point3(17.715, 16.155, 10), Point3(17.715, -8.845, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(17.715, 16.155, 6), Point3(42.715, 16.155, 6), Point3(42.715, 16.155, 10), Point3(17.715, 16.155, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(42.715, 16.155, 6), Point3(42.715, -8.845, 6), Point3(42.715, -8.845, 10), Point3(42.715, 16.155, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(42.715, -8.845, 6), Point3(42.715, -14.923, 3.5), Point3(42.715, -14.923, 10), Point3(42.715, -8.845, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(42.715, -14.923, 3.5), Point3(42.715, -21.261, 3.5), Point3(42.715, -21.261, 10), Point3(42.715, -14.923, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(42.715, -21.261, 3.5), Point3(32.715, -21.261, 3.5), Point3(32.715, -21.261, 10), Point3(42.715, -21.261, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(32.715, -21.261, 3.5), Point3(12.56, -21.261, 0), Point3(12.56, -21.261, 10), Point3(32.715, -21.261, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(12.56, -21.261, 0), Point3(-13.217, -21.261, 0), Point3(-13.217, -21.261, 10), Point3(12.56, -21.261, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(-13.217, -21.261, 0), Point3(-13.217, 19.182, 0), Point3(-13.217, 19.182, 10), Point3(-13.217, -21.261, 10)) wallCNode.addSolid(temp) temp = CollisionPolygon(Point3(-13.217, 19.182, 0), Point3(12.56, 19.182, 0), Point3(12.56, 19.182, 10), Point3(-13.217, 19.182, 10)) wallCNode.addSolid(temp) envcNodePath1 = self.env.attachNewNode(envcNode1) envcNodePath2 = self.env.attachNewNode(envcNode2) envcNodePath3 = self.env.attachNewNode(envcNode3) envcNodePath4 = self.env.attachNewNode(envcNode4) envcNodePath5 = self.env.attachNewNode(envcNode5) self.cHandler = CollisionHandlerEvent() pusher = CollisionHandlerPusher() self.wallNode = self.env.attachNewNode('wallNode') wallCNodePath = self.wallNode.attachNewNode(wallCNode) if DEBUG: wallCNodePath.show() cNode = CollisionNode("player") temp = CollisionSphere((0,-5.5,10), 4) cNode.addSolid(temp) temp = CollisionSphere((0,-0.5,10), 4) cNode.addSolid(temp) temp = CollisionSphere((0,3.5,10), 4) cNode.addSolid(temp) cNode.setIntoCollideMask(BitMask32.allOff()) #player is *only* a from object cNodePath = self.player.attachNewNode(cNode) if DEBUG: cNodePath.show() base.cTrav.addCollider(cNodePath, pusher) pusher.addCollider(cNodePath, self.player) pusher.addInPattern('%fn-into-%in') self.accept('player-into-fence', self.collideWithFence) self.accept('player-into-staticCar', self.collideOther) self.accept('player-into-droneNode', self.collideOther) self.playerLightCollision = CollisionHandlerEvent() self.playerLightCollision.addInPattern('into-%in') cNode2 = CollisionNode("playerinto") #cNode.addSolid(segment1) #cNode.addSolid(segment2) #cNode.addSolid(segment3) #cNode.addSolid(segment4) temp = CollisionSphere((0,-5.5,1), 4) cNode2.addSolid(temp) temp = CollisionSphere((0,-0.5,1), 4) cNode2.addSolid(temp) temp = CollisionSphere((0,3.5,1), 4) cNode2.addSolid(temp) cNode2.setFromCollideMask(BitMask32.allOff()) #player is *only* a from object cNodePath2 = self.player.attachNewNode(cNode2) if DEBUG: cNodePath2.show() # FLAMETHROWER COLLISIONS # left flamethrowerLeft = CollisionSegment() flamethrowerLeft.setPointA(-2 , -4, 10) flamethrowerLeft.setPointB( -2 , -20 , 10 ) # middle flamethrowerMiddle = CollisionSegment() flamethrowerMiddle.setPointA(0 , -4, 10) flamethrowerMiddle.setPointB( 0 , -20 , 10 ) # right flamethrowerRight = CollisionSegment() flamethrowerRight.setPointA(2, -4, 10) flamethrowerRight.setPointB( 2 , -20 , 10 ) flamethrowerNode = CollisionNode("flamethrower") flamethrowerNode.addSolid(flamethrowerLeft) flamethrowerNode.addSolid(flamethrowerMiddle) flamethrowerNode.addSolid(flamethrowerRight) flamethrowerNode.setIntoCollideMask(BitMask32.allOff()) flamethrowerNode.setFromCollideMask(BitMask32.allOn()) flamethrowerNodePath = self.player.attachNewNode(flamethrowerNode) #flamethrowerNodePath.show() self.flamethrowerCollision = CollisionHandlerEvent() self.flamethrowerCollision.addInPattern('into-%in') base.cTrav.addCollider(flamethrowerNodePath, self.flamethrowerCollision) self.accept('into-droneNode', self.hitEnemy) for i in range(len(self.staticCars)): staticNode = CollisionNode("staticCar") temp = CollisionSphere((0,-5.2,10), 4) staticNode.addSolid(temp) temp = CollisionSphere((0,-0.5,10), 4) staticNode.addSolid(temp) temp = CollisionSphere((0,5.5,10), 4) staticNode.addSolid(temp) staticNode.setIntoCollideMask(BitMask32.bit(1)) staticNode.setFromCollideMask(BitMask32.bit(0)) staticNodePath = self.staticCars[i].attachNewNode(staticNode) temp = CollisionTube(0,7,3,0,-6,3,3.5) sN = CollisionNode("staticTube") sN.addSolid(temp) staticNode.setFromCollideMask(BitMask32.bit(0)) sNP = self.staticCars[i].attachNewNode(sN) sN.setTag('car', str(i)) self.enemyHandler = CollisionHandlerEvent() for i in range(len(self.enemies)): collideNode = CollisionNode("droneNode") temp = CollisionSphere((0,0,10), 4) collideNode.addSolid(temp) collideNode.setIntoCollideMask(BitMask32.bit(1)) collideNode.setFromCollideMask(BitMask32.bit(0)) enemycollideNodePath = self.enemies[i].attachNewNode(collideNode) collideNode.setTag('enemy',str(i)) self.enemies[i].lightRay = CollisionSegment() self.enemies[i].lightRay.setPointA(0, -4, 4) self.enemies[i].lightRay.setPointB( 0 , -100 , 0 ) # left self.enemies[i].lightRayLeft = CollisionSegment() self.enemies[i].lightRayLeft.setPointA(0, -4, 4) self.enemies[i].lightRayLeft.setPointB( -5 , -100 , 0 ) # right self.enemies[i].lightRayRight = CollisionSegment() self.enemies[i].lightRayRight.setPointA(0, -4, 4) self.enemies[i].lightRayRight.setPointB( 5 , -100 , 0 ) self.enemies[i].lightRayNode = CollisionNode("lightRay") self.enemies[i].lightRayNode.addSolid(self.enemies[i].lightRay) self.enemies[i].lightRayNode.addSolid(self.enemies[i].lightRayLeft) self.enemies[i].lightRayNode.addSolid(self.enemies[i].lightRayRight) self.enemies[i].lightRayNode.setTag('enemy',str(i)) self.enemies[i].lightRayNode.setIntoCollideMask(BitMask32.allOff()) self.enemies[i].lightRayNodePath = self.enemies[i].attachNewNode(self.enemies[i].lightRayNode) if DEBUG: self.enemies[i].lightRayNodePath.show() base.cTrav.addCollider(self.enemies[i].lightRayNodePath, self.playerLightCollision) self.accept('into-playerinto', self.player.takeHit) def collideWithFence(self, entry): self.player.speed = self.player.speed * 0.9 if self.collideSound.status() != AudioSound.PLAYING: self.collideSound.play() def collideOther(self, entry): self.player.speed = self.player.speed * 0.9 if self.collideSound.status() != AudioSound.PLAYING: self.collideSound.play() def lightModify(self, t, which_way): if which_way: #which_way == true then make it brighter value = t/100 * MAX_LIGHT else: #which_way == true then make it darker value = (100 - t)/100 * MAX_LIGHT for light in self.flameLights: light[0].setColor(VBase4(value,value,value,1)) def startShoot(self): self.loadParticleConfig('flamethrower6.ptf') #self.lightOff.finish() #self.lightOn.start() #Get the flame noise started! self.flamethrowerSound.setLoop(True) self.flamethrowerSound.play() self.flamethrowerActive = True self.draining = False self.gasLossRate = 2.0 def stopShoot(self): self.p1.softStop() self.p2.softStop() #self.lightOn.finish() #self.lightOff.start() self.flamethrowerSound.stop() self.flamethrowerEndSound.play() self.flamethrowerActive = False self.gasLossRate = 1.0 def hitEnemy(self, entry): if self.flamethrowerActive: index = int(entry.getIntoNode().getTag('enemy')) if self.enemies[index].phase != STOPPED: self.enemies[index].prevPhase = self.enemies[index].phase self.enemies[index].phase = STOPPED self.enemies[index].headlight1.setColor(VBase4(0, 0, 0, 0)) def loadParticleConfig(self, file): self.p1.reset() self.p1 = ParticleEffect() self.p1.loadConfig(Filename(file)) self.p1.start(self.player) self.p1.setPos(-1.75, -10, 1.375) self.p1.setHpr(0, 90, 0) self.p1.setScale(2.0) self.p1.setLightOff() self.p2.reset() self.p2 = ParticleEffect() self.p2.loadConfig(Filename(file)) self.p2.start(self.player) self.p2.setPos(1.75, -10, 1.375) self.p2.setHpr(0, 90, 0) self.p2.setScale(2.0) self.p2.setLightOff() def eat(self, cEntry): """handles the player eating a smiley""" #remove target from list of targets self.targets.remove(cEntry.getIntoNodePath().getParent()) #remove from scene graph cEntry.getIntoNodePath().getParent().remove() def changeMouseCursor(self, cursorFile): if self.currIcon != cursorFile: self.currIcon = cursorFile # winprops.getParentWindow().getXSize() # print winprops.getXSize() # print "test" self.winprops.setCursorFilename(Filename.binaryFilename(cursorFile)) def deathChecker(self, task): font = loader.loadFont('fonts/beneg.ttf') #Check for out of time currTime = datetime.datetime.now() if currTime > self.startTime + self.timeLimit or self.player.totalGas >= MAX_GAS: #print "OUT OF TIME!!!!!!!!!!!" if self.finalGas is None: self.finalGas = self.player.totalGas taskMgr.doMethodLater(5, self.STOPGAME, 'tickTask') self.loading = OnscreenImage(image = 'images/victory.png', scale = (1.3333333,0, 1)) self.text = OnscreenText(text = "Gas Collected%s" %(self.finalGas), font = font, pos = (0,.2), fg = (255,255,255,1)) #Check for death elif self.player.dead: if self.finalGas is None: self.finalGas = self.player.totalGas #print "THE PLAYER IS DEAD!!!!!!!!!!" taskMgr.doMethodLater(5, self.STOPGAME, 'tickTask') self.loading = OnscreenImage(image = 'images/lose_death.png', scale = (1.3333333,0, 1)) self.text = OnscreenText(text = "Gas Collected %s" %(self.finalGas), font = font, pos = (0,.1), fg = (255,255,255,1)) elif self.player.totalGas <= 0: #print "YOU SUCK. YOU RAN OUT OF GAS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" taskMgr.doMethodLater(5, self.STOPGAME, 'tickTask') self.loading = OnscreenImage(image = 'images/lose_nogas.png', scale = (1.3333333,0, 1)) return Task.cont def updateGasBar(self, task): self.gasLevel['frameSize'] = (-1,(self.player.totalGas / MAX_GAS)*2 - 1, -1, 1) return Task.cont def STOPGAME(self, SOMETHNG): taskMgr.stop()
class MyApp(ShowBase): def __init__(self): ShowBase.__init__(self) self.disableMouse() camera.setPosHpr(0, -12, 8, 0, -35, 0) """ self.environ = self.loader.loadModel("models/environment") # Reparent the model to render. self.environ.reparentTo(self.render) # Apply scale and position transforms on the model. self.environ.setScale(0.25, 0.25, 0.25) self.environ.setPos(-8, 42, 0) self.torus = loader.loadModel("torus.egg") self.torus.reparentTo(self.render) self.torus.setPos(circPos(0,1)) self.torus.setColor(BLACK) self.torus.setScale(0.5,0.5,0.5) """ self.setupLights() self.ended = False self.currentB = False self.firstTurn = True # Since we are using collision detection to do picking, we set it up like # any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() # Make a traverser self.pq = CollisionHandlerQueue() # Make a handler # Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be positioned # relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other # collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() # Make our ray # Add it to the collision node self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) self.picker.showCollisions(render) self.whiteTurn = True # Now we create the chess board and its pieces # We will attach all of the squares to their own root. This way we can do the # collision pass just on the sqaures and save the time of checking the rest # of the scene self.batonsRoot = render.attachNewNode("batonsRoot") self.batons = [None for i in range(9)] self.torus = [[None for j in range(3)] for i in range(9)] self.org = [[[None for j in range(3)] for i in range(3)] for i in range(3)] for i in range(9): # Load, parent, color, and position the model (a single square # polygon) self.batons[i] = loader.loadModel("bois.egg") self.batons[i].reparentTo(self.batonsRoot) self.batons[i].setPos(circPos(i,0)) self.batons[i].setColor(0.75,0.5,0) self.batons[i].setScale(0.75,0.75,0.5) # Set the model itself to be collideable with the ray. If this model was # any more complex than a single polygon, you should set up a collision # sphere around it instead. But for single polygons this works # fine. self.batons[i].find("**/Cylinder").node().setIntoCollideMask( BitMask32.bit(1)) # Set a tag on the square's node so we can look up what square this is # later during the collision pass self.batons[i].find("**/Cylinder").setTag('baton', str(i)) # We will use this variable as a pointer to whatever piece is currently # in this square self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') self.accept("mouse1", self.click) self.accept("w", self.bestPossibleMove) def setupLights(self): # This function sets up some default lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.8, .8, .8, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(LVector3(0, 45, -45)) directionalLight.setColor((0.2, 0.2, 0.2, 1)) render.setLight(render.attachNewNode(directionalLight)) render.setLight(render.attachNewNode(ambientLight)) def mouseTask(self, task): # This task deals with the highlighting and dragging based on the mouse # Check to see if we can access the mouse. We need it to do anything # else if self.mouseWatcherNode.hasMouse(): # get the mouse position mpos = self.mouseWatcherNode.getMouse() # Set the position of the ray based on the mouse position self.pickerRay.setFromLens(self.camNode, mpos.getX(), mpos.getY()) if self.currentB is not False: self.batons[self.currentB].setColor(0.75,0.5,0) self.currentB = False # Do the actual collision pass (Do it only on the squares for # efficiency purposes) self.picker.traverse(self.batonsRoot) if self.pq.getNumEntries() > 0: # if we have hit something, sort the hits so that the closest # is first, and highlight that node self.pq.sortEntries() self.currentB = int(self.pq.getEntry(0).getIntoNode().getTag('baton')) # Set the highlight on the picked square self.batons[self.currentB].setColor(HIGHLIGHT) self.currentB return Task.cont def click(self): if self.currentB is not False and not self.ended: self.addTorus(self.currentB) def testMorp(self, z, y, x): print(z,y,x) print([j for j in [self.org[w][y][w]for w in range(3)]if j == None]) print([j for j in [self.org[w][y][w]for w in range(3)]if j == False]) print(len([j for j in [self.org[w][y][w]for w in range(3)]if j == False])) if len([j for j in self.org[z][y] if j == None]) == 0: if len([j for j in self.org[z][y] if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in self.org[z][y] if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[z][w][x]for w in range(3)] if j == None]) == 0: if len([j for j in [self.org[z][w][x]for w in range(3)] if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[z][w][x]for w in range(3)] if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[w][y][x]for w in range(3)] if j==None]) == 0: if len([j for j in [self.org[w][y][x]for w in range(3)] if j==False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[w][y][x]for w in range(3)] if j==True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[z][w][w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[z][w][w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[z][w][w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[z][w][2 - w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[z][w][2 - w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[z][w][2 - w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[w][w][x]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[w][w][x]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[w][w][x]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[w][2-w][x]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[w][2-w][x]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[w][2-w][x]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[w][y][w]for w in range(3)]if j == None]) == 0: print("wyw") if len([j for j in [self.org[w][y][w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[w][y][w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[2-w][y][w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[2-w][y][w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[2-w][y][w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[w][w][w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[w][w][w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[w][w][w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[2-w][w][w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[2-w][w][w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[2-w][w][w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[w][w][2-w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[w][w][2-w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[w][w][2-w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" if len([j for j in [self.org[2-w][w][2-w]for w in range(3)]if j == None]) == 0: if len([j for j in [self.org[2-w][w][2-w]for w in range(3)]if j == False]) == 0: self.ended = True return "whiteWin" elif len([j for j in [self.org[2-w][w][2-w]for w in range(3)]if j == True]) == 0: self.ended = True return "blackWin" return "launched" def addTorus(self, i): minim = False for j,x in enumerate(self.torus[i]): if x is None: minim = j break if minim is not False: print(minim) print(self.whiteTurn) print() if (self.whiteTurn and not self.firstTurn) or (self.firstTurn and not (minim == 0 and i//3 == 1 and i%3 == 1)): self.torus[i][minim] = loader.loadModel("torus.egg") self.torus[i][minim].reparentTo(self.render) self.torus[i][minim].setPos(circPos(i,j-1)) self.torus[i][minim].setColor(WHITE) self.torus[i][minim].setScale(0.75,0.75,1.5) self.org[minim][i//3][i%3] = self.whiteTurn self.whiteTurn = not self.whiteTurn if self.firstTurn: self.firstTurn = False elif not self.firstTurn: self.torus[i][minim] = loader.loadModel("torus.egg") self.torus[i][minim].reparentTo(self.render) self.torus[i][minim].setPos(circPos(i,j-1)) self.torus[i][minim].setColor(BLACK) self.torus[i][minim].setScale(0.75,0.75,1.5) self.org[minim][i//3][i%3] = self.whiteTurn self.whiteTurn = not self.whiteTurn print(self.testMorp(minim, i//3, i%3)) def possibleMoves(self, state, first): possibilities = [] for r in range(9): if (((r is not 4) and first) or (not first)) and state[2][r//3][r%3] == None: possibilities.append(r) return possibilities def bestPossibleMove(self): temp = state for p in possibleMoves(self, state, first): p = 0 def valeurBranche(self, state, first, Wturn, n): vblancs = 0 vnoirs = 0 pos = [None for p in possibleMoves(self, state, first) ] depth = pos for i,p in enumerate(possibleMoves(self, state, first)): tempEtat = addTorustemp(p, WTurn, state) temp = testMorptemp(tempEtat) if temp == 1 and Wturn: pos[i] = 1 depth[i] = n + 1 elif temp ==0 and Wturn: if first: temp2 = valeurBranche(self, temp,not first, not Wturn,n+1) if not (temp2[0] == 0): pos[i] = -temp2[0] depth[i] = temp2[1] else: pos[i] = 0 depth[i] = temp2[1] else: temp2 = valeurBranche(self, temp, first, not Wturn,n+1) if not (temp2[0] == 0): pos[i] = -temp2[0] depth[i] = temp2[1] else : pos[i] = 0 depth[i] = temp2[1] elif temp == 2 and (not Wturn): pos[i] = 1 elif temp ==0 and (not Wturn): if first: temp2 = valeurBranche(self, temp,not first, not Wturn,n+1) if not (temp2[0] == 0): pos[i] = -temp2[0] depth[i] = temp2[1] else: pos[i] = 0 depth[i] = temp2[1] else: temp2 = valeurBranche(self, temp, first, not Wturn,n+1) if not (temp2[0] == 0): pos[i] = -temp2[0] depth[i] = temp2[1] else : pos[i] = 0 depth[i] = temp2[1] else: print("tu sais pas coder guillaume") if pos == []: return (0, n) else: vic = [] zero = [] for i,j in enumerate(pos): if j == 1: vic.append(i) elif j == 0: zero.append(i) if vic is not []: return 1, n elif zero is not []: dmax = 0 for i in zero: if depth[i] > dmax: dmax = depth[i] return 0, dmax else: return -1, n def addTorustemp(self, i, whiteTurn, state): minim = False temp = state for j,x in enumerate([state[w][i//3][i%3] for w in range(3)]): if x is None: minim = j break if minim is not False: print(minim) print(whiteTurn) temp[minim][i//3][i%3] = whiteTurn return temp def testMorptemp(self, torg): for m in range(27): z = m//9 y = m//3 x = m%3 if len([j for j in torg[z][y] if j == None]) == 0: if len([j for j in torg[z][y] if j == False]) == 0: return 1 elif len([j for j in torg[z][y] if j == True]) == 0: return 2 if len([j for j in [torg[z][w][x]for w in range(3)] if j == None]) == 0: if len([j for j in [torg[z][w][x]for w in range(3)] if j == False]) == 0: return 1 elif len([j for j in [torg[z][w][x]for w in range(3)] if j == True]) == 0: return 2 if len([j for j in [torg[w][y][x]for w in range(3)] if j==None]) == 0: if len([j for j in [torg[w][y][x]for w in range(3)] if j==False]) == 0: return 1 elif len([j for j in [torg[w][y][x]for w in range(3)] if j==True]) == 0: return 2 if len([j for j in [torg[z][w][w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[z][w][w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[z][w][w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[z][w][2 - w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[z][w][2 - w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[z][w][2 - w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[w][w][x]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[w][w][x]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[w][w][x]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[w][2-w][x]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[w][2-w][x]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[w][2-w][x]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[w][y][w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[w][y][w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[w][y][w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[2-w][y][w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[2-w][y][w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[2-w][y][w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[w][w][w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[w][w][w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[w][w][w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[2-w][w][w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[2-w][w][w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[2-w][w][w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[w][w][2-w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[w][w][2-w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[w][w][2-w]for w in range(3)]if j == True]) == 0: return 2 if len([j for j in [torg[2-w][w][2-w]for w in range(3)]if j == None]) == 0: if len([j for j in [torg[2-w][w][2-w]for w in range(3)]if j == False]) == 0: return 1 elif len([j for j in [torg[2-w][w][2-w]for w in range(3)]if j == True]) == 0: return 2 return 0
class World(DirectObject): def __init__(self): #turn off mouse control, otherwise camera is not repositionable self.lightables = [] self.cameraPositions = [((0, 5000, 5300), (180, -35, 0)),((0, 3000, 1300), (180, -15, 0))] self.cameraIndex = 0 base.disableMouse() base.enableParticles() self.setupLights() self.setupPicking() #Prepare the vehicular manslaughter! self.boosterLightNP = None self.flameLights = None self.player = Vehicle("models/panda-model", "panda-walk4", self) self.loadModels() # self.player.setPos(self.env.find("**/start_point").getPos()) self.player.setPos(0,0,0) self.setupIntervals() camera.reparentTo(self.player) camera.setPosHpr(0, 5000, 5300, 180, -35, 0) self.setupCollisions() render.setShaderAuto() #you probably want to use this self.keyMap = {"left":0, "right":0, "forward":0, "backwards":0} taskMgr.add(self.player.move, "moveTask") #Give the vehicle direct access to the keyMap self.player.addKeyMap(self.keyMap) self.prevtime = 0 self.isMoving = False self.speed_norm = 8 self.speed = self.speed_norm self.accept("escape", sys.exit) self.accept("arrow_up", self.setKey, ["forward", 1]) self.accept("w", self.setKey, ["forward", 1]) self.accept("arrow_right", self.setKey, ["right", 1]) self.accept("d", self.setKey, ["right", 1]) self.accept("arrow_left", self.setKey, ["left", 1]) self.accept("a", self.setKey, ["left", 1]) self.accept("arrow_down", self.setKey, ["backwards", 1]) self.accept("s", self.setKey, ["backwards", 1]) self.accept("arrow_up-up", self.setKey, ["forward", 0]) self.accept("w-up", self.setKey, ["forward", 0]) self.accept("arrow_right-up", self.setKey, ["right", 0]) self.accept("d-up", self.setKey, ["right", 0]) self.accept("arrow_left-up", self.setKey, ["left", 0]) self.accept("a-up", self.setKey, ["left", 0]) self.accept("arrow_down-up", self.setKey, ["backwards", 0]) self.accept("s-up", self.setKey, ["backwards", 0]) self.accept("mouse1", self.startShoot) self.accept("mouse1-up", self.stopShoot) self.accept("tab", self.shiftCamera) self.accept("space", self.player.startBoosters) self.accept("ate-smiley", self.eat) self.accept("ground_collide", self.player.collider) self.p1 = ParticleEffect() self.p2 = ParticleEffect() #Show collisiony stuff base.cTrav.showCollisions(render) def setupPicking(self): self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) self.targetRoot = render.attachNewNode('targetRoot') self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') def mouseTask(self, task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.picker.traverse(self.targetRoot) if self.pq.getNumEntries() > 0: self.pq.sortEntries() i = int(self.pq.getEntry(0).getIntoNode().getTag('target')) print("Found target: " + str(i)) return Task.cont def setupIntervals(self): self.lightOn = LerpFunc(self.lightModify, fromData=0, toData=100, duration=0.2, blendType='noBlend', extraArgs=[True], name="LightUp") self.lightOff = LerpFunc(self.lightModify, fromData=0, toData=100, duration=0.2, blendType='noBlend', extraArgs=[False], name="LightDown") self.cameraMove = None def setKey(self, key, value): self.keyMap[key] = value def setWorldLight(self, object): self.lightables.append(object) object.setLight(self.keyLightNP) object.setLight(self.fillLightNP) object.setLight(self.boosterLightNP) for light in self.flameLights: object.setLight(light[1]) def shiftCamera(self): if self.cameraMove: self.cameraMove.finish() old = self.cameraIndex self.cameraIndex += 1 if self.cameraIndex == len(self.cameraPositions): self.cameraIndex = 0 self.cameraMove=LerpPosHprInterval(camera, .7, self.cameraPositions[self.cameraIndex][0], self.cameraPositions[self.cameraIndex][1], camera.getPos(), camera.getHpr()) self.cameraMove.start() def loadModels(self): """loads models into the world""" #eat no longer exists? Phooey self.flameLights = [] shadowcam = Spotlight('shadowlight') shadowcam.setColor(VBase4(0,0,0,1)) lens = PerspectiveLens() shadowcam.setLens(lens) shadowcam.setAttenuation(Point3(0, 0.001, 0.001)) shadowNP = self.player.attachNewNode(shadowcam) shadowNP.setPos(0, -1400, 450) shadowNP.lookAt(self.player) shadowNP.setScale(200) shadowNP.node().setShadowCaster(True) self.flameLights.append((shadowcam, shadowNP)) for i in range(2): slight = PointLight('plight') slight.setColor(VBase4(0, 0, 0, 1)) slight.setAttenuation(Point3(0, 0.001, 0.001)) slnp = self.player.attachNewNode(slight) slnp.setPos(0, -750 - (950 * i), 450) slnp.setHpr(180, 0, 0) slnp.setScale(200) self.flameLights.append((slight, slnp)) self.player.setupBooster() #self.env = loader.loadModel("models/environment") #self.env.reparentTo(render) #self.env.setScale(.25) #self.env.setPos(-8, 42, 0) self.env = loader.loadModel("models/terrain2") self.env.reparentTo(render) self.env.setPos(0,0,0) self.setWorldLight(self.env) #load targets self.targets = [] for i in range (10): target = loader.loadModel("smiley") target.setScale(.5) target.setPos(random.uniform(-20, 20), random.uniform(-15, 15), 2) target.reparentTo(self.targetRoot) self.targets.append(target) self.setWorldLight(target) def setupLights(self): #ambient light self.ambientLight = AmbientLight("ambientLight") #four values, RGBA (alpha is largely irrelevent), value range is 0:1 self.ambientLight.setColor((.10, .10, .10, 1)) self.ambientLightNP = render.attachNewNode(self.ambientLight) #the nodepath that calls setLight is what gets illuminated by the light render.setLight(self.ambientLightNP) #call clearLight() to turn it off self.keyLight = DirectionalLight("keyLight") self.keyLight.setColor((.20,.20,.20, 1)) self.keyLightNP = render.attachNewNode(self.keyLight) self.keyLightNP.setHpr(0, -26, 0) self.fillLight = DirectionalLight("fillLight") self.fillLight.setColor((.05,.05,.05, 1)) self.fillLightNP = render.attachNewNode(self.fillLight) self.fillLightNP.setHpr(30, 0, 0) def drive(self): """compound interval for driveing""" #some interval methods: # start(), loop(), pause(), resume(), finish() # start() can take arguments: start(starttime, endtime, playrate) dist = 5 angle = deg2Rad(self.player.getH()) dx = dist * math.sin(angle) dy = dist * -math.cos(angle) playerdrive = Parallel(self.player.posInterval(1, (self.player.getX() + dx, self.player.getY() + dy, 0)), \ self.player.actorInterval("drive", loop=1, duration=2)) playerdrive.start() def setupCollisions(self): #instantiates a collision traverser and sets it to the default base.cTrav = CollisionTraverser() self.cHandler = CollisionHandlerEvent() #set pattern for event sent on collision # "%in" is substituted with the name of the into object self.cHandler.setInPattern("ate-%in") cSphere = CollisionSphere((0,0,200), 450) #because the player is scaled way down self.playerRay = CollisionRay() self.playerRay.setOrigin(0,0,2000) self.playerRay.setDirection(0,0,-1) self.playerNode = CollisionNode("playerRay") self.playerNode.addSolid(self.playerRay) self.playerNode.setFromCollideMask(BitMask32.bit(0)) self.playerNode.setIntoCollideMask(BitMask32.allOff()) self.playerNodePath = self.player.attachNewNode(self.playerNode) self.playerNodePath.show() self.playerGroundHandler = CollisionHandlerFloor() self.playerGroundHandler.addCollider(self.playerNodePath, self.player) base.cTrav.addCollider(self.playerNodePath, self.playerGroundHandler) cNode = CollisionNode("player") cNode.addSolid(cSphere) cNode.setIntoCollideMask(BitMask32.allOff()) #player is *only* a from object #cNode.setFromCollideMask(BitMask32.bit(0)) cNodePath = self.player.attachNewNode(cNode) #registers a from object with the traverser with a corresponding handler base.cTrav.addCollider(cNodePath, self.cHandler) i = 0 for target in self.targets: cSphere = CollisionSphere((0,0,0), 2) cNode = CollisionNode("smiley") cNode.addSolid(cSphere) cNode.setIntoCollideMask(BitMask32.bit(1)) cNode.setTag('target', str(i)) cNodePath = target.attachNewNode(cNode) i += 1 def lightModify(self, t, which_way): if which_way: #which_way == true then make it brighter value = t/100 * MAX_LIGHT else: #which_way == true then make it darker value = (100 - t)/100 * MAX_LIGHT for light in self.flameLights: light[0].setColor(VBase4(value,value,value,1)) def startShoot(self): self.loadParticleConfig('flamethrower4.ptf') self.lightOff.finish() self.lightOn.start() def stopShoot(self): self.p1.softStop() self.p2.softStop() self.lightOn.finish() self.lightOff.start() def loadParticleConfig(self, file): self.p1.reset() self.p1 = ParticleEffect() self.p1.loadConfig(Filename(file)) self.p1.start(self.player) self.p1.setPos(-250, -700, 275) self.p1.setHpr(0, 90, 0) self.p1.setScale(200) self.p1.setLightOff() self.p2.reset() self.p2 = ParticleEffect() self.p2.loadConfig(Filename(file)) self.p2.start(self.player) self.p2.setPos(250, -700, 275) self.p2.setHpr(0, 90, 0) self.p2.setScale(200) self.p2.setLightOff() def eat(self, cEntry): """handles the player eating a smiley""" #remove target from list of targets self.targets.remove(cEntry.getIntoNodePath().getParent()) #remove from scene graph cEntry.getIntoNodePath().getParent().remove()
class mode_head(): def __init__(self,base,Golog, folder_path = None, parent = None): # Set up basic attributes self.base = base self.golog = Golog self.bools = {'textboxes':True} self.buttons = dict() self.window_tasks = dict() self.bt = None self.mw = None self.listener = DirectObject() self.folder_path = folder_path #absolute path of golog folder '/path/to/golog/folder' if self.folder_path: self.file_path = os.path.abspath(self.folder_path + '/' + self.golog.label+ '.golog') autosave = True self.has_window = False self.parent = parent #for autosaving up to original golog self.reset = self.basic_reset self.garbage = [] #list of deleted math_data/graphics_data etc. #create a 2d rende self.render2d = NodePath('2d render') self.camera2D = self.render2d.attachNewNode(Camera('2d Camera')) self.camera2D.setDepthTest(False) self.camera2D.setDepthWrite(False) lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setNearFar(-1000, 1000) self.camera2D.node().setLens(lens) # make a dictionary of mode_heads in the underlying golog if hasattr(self.golog,'mode_heads'): m = 0 while m in self.golog.mode_heads.keys(): m+=1 #get smallest unused mode_head index self.index = m self.label = self.golog.label+"_mode_head_"+str(self.index) self.golog.mode_heads[self.index] = self else: self.golog.mode_heads = dict() self.index = 0 self.label = self.golog.label+"_mode_head_"+ str(self.index) self.golog.mode_heads[self.index] = self ########## ### set up collision handling ### self.queue = CollisionHandlerQueue() ### set up selection tools self.create_list = [[],[]] #select nodes and add to create_list in order to create higher simplecies self.bools = {'selecter':False,'textboxes':True,'shift_clicked':False} #some bools that will be usefull self.dict = {'shift_pt':[None,None]} # set up mouse picker self.pickerNode = CollisionNode('mouseRay') self.pickerNP = self.golog.camera.attachNewNode(self.pickerNode) #attach collision node to camera self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.pickerNode.set_into_collide_mask(0) #so that collision rays don't collide into each other if there are two mode_heads self.golog.cTrav.addCollider(self.pickerNP,self.queue) #send collisions to self.queue # set up plane for picking self.planeNode = self.golog.render.attachNewNode("plane") self.planeNode.setTag("mode_head",self.label) # tag to say it belongs to this mode_head self.planeNode.setTag("mode_node", 'plane') self.planeFromObject = self.planeNode.attachNewNode(CollisionNode("planeColNode")) self.planeFromObject.node().addSolid(CollisionPlane(Plane(Vec3(0,-1,0),Point3(0,0,0)))) self.plane = self.planeFromObject.node().getSolid(0) ### # set up preview text self.textNP = self.render2d.attachNewNode(TextNode('text node')) self.textNP.setScale(.2) self.textNP.setPos(-1,0,0) self.textNP.show() #set up dragging info self.grabbed_dict = dict() self.drag_dict = dict() #a mapping from selected nodes to their original positions for dragging self.mouse_down_loc = (0,0,0) self.lowest_level = 3 def open_math_data(self,math_data): if math_data.type == 'golog': golog_dict = math_data() #calls the mathdata (which is a golog_dict) subgolog = golog_dict['golog'] subfolder_path = golog_dict['folder_path'] #### just to be safe, but probably not needed subgolog_folder_path = os.path.join(self.folder_path,'subgologs') if not os.path.exists(subgolog_folder_path): os.mkdir(subgolog_folder_path) #### folder_path = os.path.join(self.folder_path, *golog_dict['folder_path']) print(folder_path) controllable_golog = mode_head(self.base, subgolog, folder_path = folder_path, parent = self) window_manager.modeHeadToWindow(self.base, controllable_golog) if math_data.type == 'file': file_name, file_extension = os.path.splitext(math_data()[-1]) # if file_extension == '.txt': # tk_funcs.edit_txt(os.path.join(self.folder_path,*math_data())) # else: #prompt user to select a program tk_funcs.run_program('',os.path.join(self.folder_path,*math_data())) if math_data.type == 'latex': file_dict = math_data() tex_folder = os.path.join(os.path.abspath(self.folder_path),*file_dict['folder']) tex_file = os.path.join(os.path.abspath(self.folder_path),*file_dict['tex']) if 'pdf' in file_dict.keys(): pdf_file = os.path.join(self.folder_path, *file_dict['pdf']) elif os.path.exists(tex_file.split('.tex')[0]+'.pdf'): #if there is a pdf in the folder with the same name file_dict['pdf'] = file_dict['tex'] file_dict['pdf'][-1] = file_dict['pdf'][-1].split('.tex')[0]+'.pdf' #change extension to .pdf pdf_file = os.path.join(self.folder_path, *file_dict['pdf']) else: pdf_file = None tk_funcs.pdf_or_tex(pdf_file, tex_file) if math_data.type == 'weblink': open_new_tab(math_data()) def update_math_data(self,simplex, math_data_type, **kwargs): if autosave == True: self.save() if 'label' in kwargs: simplex.label = kwargs['label'] self.golog.Simplex_to_Graphics[simplex].textNP.node().setText(simplex.label) self.golog.text_preview_set(self.bools['textboxes']) if simplex.math_data.type != 'None': answer = tk_funcs.are_you_sure('are you sure you want to delete the current math_data?') if answer == 'yes': self.delete_math_data(simplex) else: return if math_data_type == 'None': simplex.math_data = hcat.Math_Data(type = 'None') if math_data_type == 'golog': #create a path for all subgologs, if it doesn't already exist subgolog_folder_path = os.path.join(self.folder_path,'subgologs') if not os.path.exists(subgolog_folder_path): os.mkdir(subgolog_folder_path) new_golog = golog.golog(self.base, label = kwargs['label']) #create a new golog #create a unique folder path list in subgolog_folder_path unique_path = tk_funcs.unique_path(subgolog_folder_path,[kwargs['label']]) new_folder_path = ['subgologs', *unique_path] os.mkdir(os.path.join(self.folder_path , *new_folder_path)) #make the directory as above #create a new golog save at new_folder_path/label.golog new_save_location = os.path.join(self.folder_path, *new_folder_path, kwargs['label']+'.golog') #new_save_location should be a subfolder of subgologs gexport(new_golog, new_save_location) #math data is a dictionary of the physical golog and it's relative save path list golog_dict = {'golog':new_golog, 'folder_path':new_folder_path} simplex.math_data = hcat.Math_Data(math_data = golog_dict, type = 'golog') if math_data_type == 'file': if not os.path.exists(os.path.join(self.folder_path,'files')): os.mkdir(os.path.join(self.folder_path,'files')) file_folder_path = ['files'] file_location = tk_funcs.ask_file_location() if not file_location: return #if user cancels file_name = os.path.split(file_location)[1] #file name with extension file_path = tk_funcs.unique_path(os.path.join(self.folder_path),[*file_folder_path, file_name]) #get a unique file path starting from the file_folder copyfile(file_location, os.path.join(self.folder_path,*file_path)) simplex.math_data = hcat.Math_Data(math_data = file_path, type = 'file') #? add handler for if user exits text editor #? make asynchronous if math_data_type == 'latex': #ensure latex folder exists if not os.path.exists(os.path.join(self.folder_path,'latex')): os.mkdir(os.path.join(self.folder_path,'latex')) # create a uniquely named folder in self.folder_path/latex/ based on simplex.label tex_folder_path = tk_funcs.unique_path(root = self.folder_path, path = ['latex',simplex.label]) os.mkdir(os.path.join(self.folder_path, *tex_folder_path)) #create a tex file in tex folder tex_file_path = [*tex_folder_path, simplex.label+'.tex'] # ask if want new or to load one location = tk_funcs.load_tex(self.folder_path) # if new, returns True and copies template tex file # if load, returns a path and copies the path into tex_file_path true_path = os.path.join(self.folder_path,*tex_file_path) if location == True: copyfile(os.path.abspath('./misc_data/config_files/template.tex'), true_path) # # open( true_path , 'w').close() if isinstance(location, str): copyfile(location, true_path) # make a file dictionary with just tex file in it file_dict = {'tex':tex_file_path, 'folder':tex_folder_path} simplex.math_data = hcat.Math_Data(math_data = file_dict, type = 'latex') if math_data_type == 'weblink': weblink = tk_funcs.ask_weblink() simplex.math_data = hcat.Math_Data(math_data = weblink, type = 'weblink') if math_data_type == 'text': #create a text file if not os.path.exists(os.path.join(self.folder_path,'files')): os.mkdir(os.path.join(self.folder_path,'files')) file_folder_path = ['files'] file_path = tk_funcs.unique_path(root = self.folder_path, path = ['files',simplex.label+'.txt' ]) with open(os.path.join(self.folder_path, *file_path),'w') as file: pass #create a math_data for it simplex.math_data = hcat.Math_Data(math_data = file_path, type = 'file') #save golog if autosave == True: self.save() return simplex.math_data def delete_math_data(self,simplex,**kwargs): simplex.math_data.delete(self.folder_path) simplex.math_data = hcat.Math_Data(type = 'None') def setup_window(self, windict): self.windict = windict for button in self.buttons.keys(): self.listener.accept(self.windict['bt'].prefix+button, self.buttons[button], extraArgs = [self.windict['mw']]) for window_task in self.window_tasks.keys(): base.taskMgr.add(self.window_tasks[window_task], window_task, extraArgs = [self.windict['mw']], appendTask = True) self.has_window = True def save(self, *ask): #if has a parent, save the parent. If not, save itself if not self.parent: #if doesn't have a parent mode_head, save parent #if we pass something to ask it will ask (this includes mousewatchers) if ask: save_location = tk_funcs.ask_file_location(initial_dir = self.folder_path) if not save_location: return #if user cancels print('saving to:\n'+save_location) gexport(self.golog, self.folder_path) else: gexport(self.golog, self.file_path) else: self.parent.save()# if parent has no mode_head, save itself #basic reset function which shuts off the listener and removes button bindings. Should only be called if no "reset" function exists def basic_reset(self,*args): self.buttons = dict() self.window_tasks = dict() self.listener.ignoreAll() #function to call before deleting the mode_head, for example when closing a window def clean(self): self.reset() #close window if self.has_window == True: if hasattr(mode_head,'windict'): if 'win' in mode_head.windict.keys(): self.base.closeWindow(mode_head.windict['win'], keepCamera = True, removeWindow = True) self.has_window = False del self.golog.mode_heads[self.index] del self.reset # function to return the only the relevant collision data from the mouseRay def get_relevant_entries(self, mw): # get list of entries by distance if not mw.node().hasMouse(): return mpos = mw.node().getMouse() self.pickerRay.setFromLens(self.golog.camNode,mpos.getX(),mpos.getY()) #mouse ray goes from camera through the 'lens plane' at position of mouse self.golog.cTrav.traverse(self.golog.render) self.queue.sortEntries() # get the first relevant node traversed by mouseRay #### ignore everything with a mode_head tag that is not defined by this mode_head for e in self.queue.getEntries(): if e.getIntoNodePath().getParent().hasTag("mode_head"): if e.getIntoNodePath().getParent().getTag("mode_head") == self.label: return (e.getIntoNodePath().getParent(), e.getIntoNodePath().getParent().getTag("mode_node"),e.getSurfacePoint(e.getIntoNodePath())) break else: entry = e break #return node create_list in the golog entryNP = entry.getIntoNodePath().getParent() if entryNP.hasTag('level'): return (entryNP, entryNP.getTag('level'),entryNP.getPos()) #function to 'pick up' a node by adding it to the dragged dictionary def pickup(self, mw): if not mw.node().hasMouse(): return (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) ### get position on plane for mouseloc mpos = mw.node().getMouse() self.pickerRay.setFromLens(self.golog.camNode,mpos.getX(),mpos.getY()) #mouse ray goes from camera through the 'lens plane' at position of mouse self.golog.cTrav.traverse(self.golog.render) self.queue.sortEntries() for e in self.queue.getEntries(): if e.getIntoNodePath().getParent().hasTag("mode_head"): if e.getIntoNodePath().getParent().getTag("mode_head") == self.label: if e.getIntoNodePath().getParent().getTag("mode_node") == 'plane': self.mouse_down_loc = e.getSurfacePoint(e.getIntoNodePath()) break #if selected node is in the drag_dict, use it to set a mouse location # if entryNP in self.drag_dict.keys(): self.mouse_down_loc = entryNP.getPos() if node_type in ['0','1']: self.grabbed_dict = {'graphics': self.golog.NP_to_Graphics[entryNP],'dragged':False, 'orig_pos': entryNP.getPos()} if node_type == 'plane': for node in self.create_list[0]: node.setColorScale(1,1,1,1) #turn white self.create_list = [[],[]] for node in self.drag_dict.keys(): node.setColorScale(1,1,1,1) self.drag_dict = dict() #drag dict is used for multi-dragging a = self.plane.distToPlane(self.golog.camera.getPos())/(2*self.golog.camera.node().getLens().getNear())# ratio for camera movement self.grabbed_dict = {'graphics':self.golog.camera, 'dragged':False, 'orig_pos': self.golog.camera.getPos(), 'mpos': LPoint3f(a*mpos.getX(),a*0,a*mpos.getY())} self.lowest_level = 3 #function to 'put down' a node, returns true if it's dragged something def putdown(self, mw): for node in self.drag_dict.keys(): self.drag_dict[node] = self.golog.NP_to_Graphics[node].graphics_kwargs['pos'] self.lowest_level = min(self.lowest_level, int(node.getTag('level'))) if 'dragged' in self.grabbed_dict.keys(): if self.grabbed_dict['dragged'] == True: self.grabbed_dict = dict() return True self.grabbed_dict = dict() #function to select a node and add a 1-simplex between 2 create_list 0-simplecies def select_for_creation(self, mw): if not mw.node().hasMouse(): return #remove selected for node in self.drag_dict.keys(): node.setColorScale(1,1,1,1) self.drag_dict = dict() self.lowest_level = 3 ### selection ### (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == '0':# and set(create_list[1:]) = {[]}: if entryNP not in self.create_list[0]: #? don't just append, re-sort self.create_list[0].append(entryNP) entryNP.setColorScale(1,0,0,0) #turn red if len(self.create_list[0]) == 2: # NP -> graphics -> simplex faces = tuple([self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[faceNP]] for faceNP in self.create_list[0][-1:-3:-1]]) asked_list = tk_funcs.ask_math_data('1-Simplex') if not asked_list: #[label, math_data_type,] return simplex = self.golog.add(faces, label = asked_list[0]) #reversed create_list objects and creates a 1 - simplex from them self.update_math_data(simplex, asked_list[1], label = asked_list[0]) for node in self.create_list[0]: node.setColorScale(1,1,1,1) self.create_list[0] = [] #reset create_list #open math_data of simplex under mouse def mouse_open(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) # if spaced on a 0 simplex, open it's math data, or create it if node_type in ['0','1']: simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] if not simplex.math_data(): asked_list = tk_funcs.ask_math_data(simplex.label) if not asked_list: return self.update_math_data(simplex, math_data_type = asked_list[1], label = asked_list[0]) #? make asynchronous else: self.open_math_data(simplex.math_data) # delete a simplex, prompt to delete the math_data and (recursively) it's supported simplecies def delete(self,mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type in ['0','1']: #warning about deleting supported simplecies graphics = self.golog.NP_to_Graphics[entryNP] simplex = self.golog.Graphics_to_Simplex[graphics] if simplex.supports: answer = tk_funcs.are_you_sure(simplex.label+' still supports other simplecies. Are you sure you wish to delete?') if answer != 'yes': return self.delete_math_data(simplex) graphics._remove() #update the math_data of a simplex under the mouse def mouse_update(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type in ['0','1']: simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] asked_list = tk_funcs.ask_math_data(simplex.label) if not asked_list: return self.update_math_data(simplex, math_data_type = asked_list[1], label = asked_list[0]) #create a simplex given a mouse position def create(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == 'plane': for node in self.create_list[0]: node.setColorScale(1,1,1,1) #turn white self.create_list = [[],[]] asked_list = tk_funcs.ask_math_data('0-Simplex') if not asked_list: return #if canceled, do not create a simplex simplex = self.golog.add(0, pos = entry_pos, label = asked_list[0]) #create a simplex self.update_math_data(simplex, math_data_type = asked_list[1], label = asked_list[0]) #function which checks the drag dictionary and drags stuff def drag(self, mw): if self.grabbed_dict: #get mouse_loc mpos = mw.node().getMouse() self.pickerRay.setFromLens(self.golog.camNode,mpos.getX(),mpos.getY()) #mouse ray goes from camera through the 'lens plane' at position of mouse self.golog.cTrav.traverse(self.golog.render) self.queue.sortEntries() mouseloc = None for e in self.queue.getEntries(): if e.getIntoNodePath().getParent().getTag("mode_node") == 'plane': mouseloc = e.getSurfacePoint(e.getIntoNodePath()) break if not mouseloc:return self.bools['dragging'] = True # if there is something in the drag dict (for multiselect) if self.drag_dict: self.grabbed_dict['dragged'] = True #only drag lowest dim simplecies for node in self.drag_dict.keys(): if int(node.getTag('level')) == self.lowest_level: self.golog.NP_to_Graphics[node].update({'pos':self.drag_dict[node]+mouseloc-self.mouse_down_loc}) #if nothing is selected, drag the thing below you elif self.grabbed_dict['graphics'] != self.golog.camera: #radius of drag selection offset = mouseloc - self.grabbed_dict['graphics'].parent_pos_convolution() delta = offset - self.grabbed_dict['orig_pos'] norm = delta.getX()**2 +delta.getY()**2 +delta.getZ()**2 if self.grabbed_dict['dragged'] == True or norm > 1: self.grabbed_dict['dragged'] = True #if offset magnitude is greater than 1 or dragged == true, actually drag it if self.grabbed_dict['dragged'] == True: self.grabbed_dict['graphics'].update({'pos':offset}) elif self.grabbed_dict['graphics'] == self.golog.camera: a = self.plane.distToPlane(self.golog.camera.getPos())/(2*self.golog.camera.node().getLens().getNear())# ratio for camera movement self.golog.camera.setPos(self.grabbed_dict['orig_pos']-(a*mpos.getX(),0,a*mpos.getY())+self.grabbed_dict['mpos']) #send preview of math_data of simplex under the mouse def preview(self, mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == '0': simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] elif node_type == '1': #? again consider what needs to be shown with 1-simplecies simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] else: return # set preview up if self.has_window: if simplex.math_data.type == 'golog': self.windict['preview_dr'].setCamera(simplex.math_data()['golog'].camera) else: self.windict['preview_dr'].setCamera(self.camera2D) self.textNP.node().setText("label:\n" +simplex.label+"\n\n math data type:\n" + simplex.math_data.type) return def pprint(self,mw): (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) if node_type == '0': simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] elif node_type == '1': #? again consider what needs to be shown with 1-simplecies simplex = self.golog.Graphics_to_Simplex[self.golog.NP_to_Graphics[entryNP]] else: return simplex.pprint() #tool for selecting multiple simplecies def multi_select(self,mw): if isinstance(mw, NodePath): entryNP = mw node_type = entryNP.getTag('level') else: return #reset select and create if node_type in ['0','1']: if entryNP in self.drag_dict.keys(): del self.drag_dict[entryNP] entryNP.setColorScale(1,1,1,1) self.lowest_level = min([*[int(node.getTag('level')) for node in self.drag_dict.keys()],3]) else: self.drag_dict[entryNP] = self.golog.NP_to_Graphics[entryNP].graphics_kwargs['pos'] entryNP.setColorScale(.5,.5,0,1) self.lowest_level = min(self.lowest_level, int(entryNP.getTag('level'))) def shift_box(self,mw): if None in self.dict['shift_pt']: self.dict['shift_pt'] = [None,None] return pt_1 = self.dict['shift_pt'][0] pt_2 = self.dict['shift_pt'][1] if sum([x**2 for x in list(pt_2-pt_1)]) <= 1: (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) self.multi_select(entryNP) #create vectors N = self.planeFromObject.node().getSolid(0).getNormal() x = [(pt_2-pt_1).getX(),0,0] z = [0,0,(pt_2-pt_1).getZ()] campos = list(self.golog.camera.getPos()) for simplex in self.golog.sSet.rawSimps: node = self.golog.Simplex_to_Graphics[simplex].node_list[0] P = list(node.getPos()) if t_int(pt_1,x,z,campos,P): self.multi_select(node) self.dict['shift_pt'] = [None,None] def consolidate(self, selected = False,sel_simp = None): #ask for label, or cancel G = self.golog # if a collection isn't provided, use the (multi-select) drag dictionary if not selected: selected = [G.Graphics_to_Simplex[G.NP_to_Graphics[node]] for node in self.drag_dict.keys()] if not selected: return #return if there was nothing passed, and nothing in the drag_dict for simp in selected: for face in simp.faces: if face not in selected: selected.append(face) # #? select a simplex to consolidate into # sel_simp = None # for simp in selected: # if simp.math_data.type == 'None': # sel_simp = simp #make a golog from selected new_golog = golog.golog(self.base, label ='test') def add(simplex): for face in simplex.faces: add(face) new_golog.add(simplex, pos = G.Simplex_to_Graphics[simplex].graphics_kwargs['pos']) for simplex in selected: add(simplex) #consolidate into 1 simplex if sel_simp: #consolidate into sel_simp subgolog_folder_path = os.path.join(self.folder_path,'subgologs') unique_path = tk_funcs.unique_path(subgolog_folder_path,[sel_simp.label]) new_folder_path = ['subgologs', *unique_path] sel_simp.math_data = hcat.Math_Data(math_data = {'golog':new_golog, 'folder_path':new_folder_path}, type = 'golog') #? remove simplexes and place at selected simplex location return sel_simp #create an entirely new simplex to put the golog into else: #? ask for label / cancel label = "test" subgolog_folder_path = os.path.join(self.folder_path,'subgologs') unique_path = tk_funcs.unique_path(subgolog_folder_path,[label]) new_folder_path = ['subgologs', *unique_path] #create a simplex with average position of things in golog avg = LPoint3f(*[sum([G.Simplex_to_Graphics[simplex].graphics_kwargs['pos'][i]/len(new_golog.sSet.simplecies[()]) for simplex in new_golog.sSet.simplecies[()]]) for i in range(3)]) s = self.golog.add(0, label ='test', math_data = hcat.Math_Data(math_data = {'golog':new_golog, 'folder_path':new_folder_path}, type = 'golog'), pos = LPoint3f(avg)) return s ########## BEGIN DEFINING MODES ########## #selection and creation mode def selection_and_creation(self, windict): def mouse1(mw): if not mw: return self.bools['shift_clicked'] = False self.pickup(mw) def shift_mouse1(mw): if not mw: return #on click, begin a rectagle dragging function (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) self.dict['shift_pt'][0] = entry_pos self.bools['shift_clicked'] = True def mouse1_up(mw): dropped_bool = self.putdown(mw) if self.bools['shift_clicked']: (entryNP, node_type, entry_pos) = self.get_relevant_entries(mw) self.dict['shift_pt'][1] = entry_pos self.shift_box(mw) self.bools['shift_clicked'] = False elif dropped_bool: pass else: self.select_for_creation(mw) def space(mw): if not mw: return self.mouse_open(mw) def u(mw): if not mw: return #? do a change not just update self.mouse_update(mw) def c(mw): self.consolidate() def p(mw): if not mw: return self.pprint(mw) def mouse3(mw): if not mw: return self.create(mw) def backspace(mw): if not mw: return self.delete(mw) def mouse_watch_task(mw,task): if not mw: return task.cont if not mw.node().hasMouse(): return task.cont self.drag(mw) self.preview(mw) return task.cont def wheel_up(mw): if not mw:return a = self.plane.distToPlane(self.golog.camera.getPos())/(2*self.golog.camera.node().getLens().getNear())# ratio for camera movement self.golog.camera.setPos( self.golog.camera.getPos() + (0,10,0) ) #fix for offset by storing a global camera - plane ratio def wheel_down(mw): if not mw:return self.golog.camera.setPos( self.golog.camera.getPos() - (0,10,0) ) def reset(*args): self.buttons = dict() self.window_task = dict() self.reset = self.basic_reset self.reset = reset self.buttons = {'mouse1':mouse1, 'mouse1-up':mouse1_up, 'mouse3':mouse3,'c':c, 'space':space, 'escape':self.reset, 's':self.save, 'u':u,'backspace':backspace, 'shift-mouse1':shift_mouse1,'p':p,'wheel_up':wheel_up, "wheel_down":wheel_down} self.window_tasks = {'mouse_watch_task':mouse_watch_task} self.setup_window(windict)
class MyApp(ShowBase): def __init__(self): ShowBase.__init__(self) # Create a traverser and a handler self.cTrav = CollisionTraverser() self.cQueue = CollisionHandlerQueue() # Create the collision node that will store the collision # ray solid self.pickerNode = CollisionNode('mouseRay') # Set bitmask for efficiency, only hit from objects with same mask self.pickerNode.setFromCollideMask(BitMask32.bit(1)) # Attach collision node to camera, since that is the source self.pickerNP = camera.attachNewNode(self.pickerNode) # Add collision solid(ray) to collision node self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) # Add collidable node(pickerNP) to the traverser # Collisions detected when traversed will go to cQueue self.cTrav.addCollider(self.pickerNP, self.cQueue) # Create visible sphere self.tmpSphere = self.loader.loadModel("models/misc/sphere") self.tmpSphere.reparentTo(self.render) self.tmpSphere.setColor(1, 1, 1, 1) self.tmpSphere.setPos(0, 100, 0) # Create collision sphere and attach to tmpSphere cSphere = CollisionSphere(0, 0, 0, 3) cnodePath = self.tmpSphere.attachNewNode(CollisionNode('cnode')) # Add collision solid(sphere) to collision node # Because tmpSphere/cSphere is child of render, which we traverse # later, it becomes a from collider automatically, we don't # need to addCollider since that is only for from collision nodes cnodePath.node().addSolid(cSphere) # Set bitmask to match the from collisionnode mask for efficiency cnodePath.setCollideMask(BitMask32.bit(1)) # Show the collision sphere visibly, for debugging. cnodePath.show() # Set a custom tag on the collision node which becomes available # inside the collision event stored in the collision handler cnodePath.setTag('someTag', '1') # Add task to run every frame - set collision solid(ray) # to start at the current camera position, self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') def mouseTask(self, task): if self.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() # Set collision ray to start at camera lens and endpoint at mouse self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) # Perform the collision traverse - follows the ray and logs # all collisions to the handler set earlier self.cTrav.traverse(self.render) # Get info from entries and use them if self.cQueue.getNumEntries() > 0: self.cQueue.sortEntries() print("Collision!") entry = self.cQueue.getEntry(0) print(entry) someTag = int(entry.getIntoNode().getTag('someTag')) print(someTag) # Collision detected, mouse must be over sphere self.tmpSphere.setColor(0, 1, 0, 1) else: # No collisions, mouse is not over sphere, unset color # Not ideal to "unset" the color each frame self.tmpSphere.setColor(1, 1, 1, 1) return task.cont
class LocalToon(DistributedToon): neverDisable = 1 def __init__(self, cr): try: self.LocalToon_initialized return except: self.LocalToon_initialized = 1 DistributedToon.__init__(self, cr) self.avatarChoice = cr.localAvChoice self.smartCamera = SmartCamera() self.chatInput = ChatInput() self.moneyGui = MoneyGui() self.laffMeter = LaffOMeter() self.positionExaminer = PositionExaminer() self.friendRequestManager = FriendRequestManager() self.friendsList = FriendsList() self.panel = ToonPanel() friendsgui = loader.loadModel('phase_3.5/models/gui/friendslist_gui.bam') self.friendButton = DirectButton(geom=(friendsgui.find('**/FriendsBox_Closed'), friendsgui.find('**/FriendsBox_Rollover'), friendsgui.find('**/FriendsBox_Rollover')), text=('', 'Friends', 'Friends', ''), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.065, text_pos=(0, -0.2), relief=None, parent=base.a2dTopRight, pos=(-0.18, 0.0, -0.17), command=self.friendsButtonClicked, scale=0.75) friendsgui.removeNode() del friendsgui self.hideFriendButton() self.runSfx = base.loadSfx('phase_3.5/audio/sfx/AV_footstep_runloop.wav') self.runSfx.setLoop(True) self.walkSfx = base.loadSfx('phase_3.5/audio/sfx/AV_footstep_walkloop.wav') self.walkSfx.setLoop(True) self.controlManager = ControlManager.ControlManager(True, False) self.offset = 3.2375 self.movementKeymap = {'forward': 0, 'backward': 0, 'left': 0, 'right': 0, 'jump': 0} self.avatarMovementEnabled = False self.isMoving_forward = False self.isMoving_side = False self.isMoving_back = False self.isMoving_jump = False self.pieThrowBtn = None self.myBattle = None self.invGui = None self.pickerTrav = None self.pickerRay = None self.pickerRayNode = None self.pickerHandler = None self.rolledOverTag = None self.inTutorial = False self.hasDoneJump = False self.lastState = None self.lastAction = None return def hasDiscoveredHood(self, zoneId): return zoneId in self.hoodsDiscovered def hasTeleportAccess(self, zoneId): return zoneId in self.teleportAccess def tutorialCreated(self, zoneId): self.cr.tutorialCreated(zoneId) def friendsButtonClicked(self): self.hideFriendButton() self.friendsList.fsm.request('onlineFriendsList') def hideFriendButton(self): self.friendButton.hide() def showFriendButton(self): self.friendButton.show() def gotoNode(self, node, eyeHeight = 3): possiblePoints = (Point3(3, 6, 0), Point3(-3, 6, 0), Point3(6, 6, 0), Point3(-6, 6, 0), Point3(3, 9, 0), Point3(-3, 9, 0), Point3(6, 9, 0), Point3(-6, 9, 0), Point3(9, 9, 0), Point3(-9, 9, 0), Point3(6, 0, 0), Point3(-6, 0, 0), Point3(6, 3, 0), Point3(-6, 3, 0), Point3(9, 9, 0), Point3(-9, 9, 0), Point3(0, 12, 0), Point3(3, 12, 0), Point3(-3, 12, 0), Point3(6, 12, 0), Point3(-6, 12, 0), Point3(9, 12, 0), Point3(-9, 12, 0), Point3(0, -6, 0), Point3(-3, -6, 0), Point3(0, -9, 0), Point3(-6, -9, 0)) for point in possiblePoints: pos = self.positionExaminer.consider(node, point, eyeHeight) if pos: self.setPos(node, pos) self.lookAt(node) self.setHpr(self.getH() + random.choice((-10, 10)), 0, 0) return self.setPos(node, 0, 0, 0) def setFriendsList(self, friends): DistributedToon.setFriendsList(self, friends) self.cr.friendsManager.d_requestFriendsList() self.panel.maybeUpdateFriendButton() def d_requestAddFriend(self, avId): self.sendUpdate('requestAddFriend', [avId]) def setupPicker(self): self.pickerTrav = CollisionTraverser('LT.pickerTrav') self.pickerRay = CollisionRay() rayNode = CollisionNode('LT.pickerNode') rayNode.addSolid(self.pickerRay) rayNode.setCollideMask(BitMask32(0)) rayNode.setFromCollideMask(CIGlobals.WallBitmask) self.pickerRayNode = base.camera.attachNewNode(rayNode) self.pickerHandler = CollisionHandlerQueue() self.pickerTrav.addCollider(self.pickerRayNode, self.pickerHandler) def enablePicking(self): self.accept('mouse1', self.pickedSomething_down) self.accept('mouse1-up', self.pickedSomething_up) base.taskMgr.add(self.__travMousePicker, 'LT.travMousePicker') def disablePicking(self): base.taskMgr.remove('LT.travMousePicker') self.ignore('mouse1') self.ignore('mouse1-up') def pickedSomething_down(self): if self.rolledOverTag: base.playSfx(DGG.getDefaultClickSound()) avatar = self.cr.doId2do.get(self.rolledOverTag) avatar.nameTag.setPickerState('down') def pickedSomething_up(self): if self.rolledOverTag: avatar = self.cr.doId2do.get(self.rolledOverTag) avatar.nameTag.setPickerState('up') self.panel.makePanel(self.rolledOverTag) def __travMousePicker(self, task): if not base.mouseWatcherNode.hasMouse(): return task.cont else: mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.pickerTrav.traverse(render) if self.pickerHandler.getNumEntries() > 0: self.pickerHandler.sortEntries() pickedObject = self.pickerHandler.getEntry(0).getIntoNodePath() avatarId = pickedObject.getParent().getPythonTag('avatar') if avatarId != None: for do in self.cr.doId2do.values(): if do.__class__.__name__ == 'DistributedToon': if do.doId == avatarId: if do.nameTag.getClickable() == 1: if do.nameTag.fsm.getCurrentState().getName() != 'rollover' and do.nameTag.fsm.getCurrentState().getName() != 'down': do.nameTag.setPickerState('rollover') base.playSfx(DGG.getDefaultRolloverSound()) self.rolledOverTag = avatarId break elif do.__class__.__name__ == 'DistributedToon': if do.nameTag.fsm.getCurrentState().getName() != 'up': do.nameTag.setPickerState('up') elif self.rolledOverTag: avatar = self.cr.doId2do.get(self.rolledOverTag) if avatar: if avatar.nameTag.fsm.getCurrentState().getName() != 'up': avatar.nameTag.setPickerState('up') self.rolledOverTag = None return task.cont def prepareToSwitchControlType(self): inputs = ['run', 'forward', 'reverse', 'turnLeft', 'turnRight', 'slideLeft', 'slideRight', 'jump'] for inputName in inputs: try: inputState.releaseInputs(inputName) except: pass def getBackpack(self): return DistributedToon.getBackpack(self) def setMyBattle(self, battle): self.myBattle = battle def getMyBattle(self): return self.myBattle def ghostOn(self): self.getGeomNode().setTransparency(1) self.getGeomNode().setColorScale(1, 1, 1, 0.25) def ghostOff(self): self.getGeomNode().setColorScale(1, 1, 1, 1) self.getGeomNode().setTransparency(0) def enterReadBook(self, ts = 0, callback = None, extraArgs = []): self.stopLookAround() self.b_lookAtObject(0, -45, 0) DistributedToon.enterReadBook(self, ts, callback, extraArgs) def exitReadBook(self): DistributedToon.exitReadBook(self) self.startLookAround() def getAirborneHeight(self): return self.offset + 0.025 def setupControls(self): self.walkControls = GravityWalker(legacyLifter=False) self.walkControls.setWallBitMask(CIGlobals.WallBitmask) self.walkControls.setFloorBitMask(CIGlobals.FloorBitmask) self.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed, CIGlobals.ToonJumpForce, CIGlobals.ToonReverseSpeed, CIGlobals.ToonRotateSpeed) self.walkControls.initializeCollisions(base.cTrav, self, floorOffset=0.025, reach=4.0) self.walkControls.setAirborneHeightFunc(self.getAirborneHeight) def setWalkSpeedNormal(self): self.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed, CIGlobals.ToonJumpForce, CIGlobals.ToonReverseSpeed, CIGlobals.ToonRotateSpeed) def setWalkSpeedSlow(self): self.walkControls.setWalkSpeed(CIGlobals.ToonForwardSlowSpeed, CIGlobals.ToonJumpSlowForce, CIGlobals.ToonReverseSlowSpeed, CIGlobals.ToonRotateSlowSpeed) def setupCamera(self): base.camLens.setMinFov(CIGlobals.DefaultCameraFov / (4.0 / 3.0)) base.camLens.setNearFar(CIGlobals.DefaultCameraNear, CIGlobals.DefaultCameraFar) camHeight = max(self.getHeight(), 3.0) heightScaleFactor = camHeight * 0.3333333333 defLookAt = Point3(0.0, 1.5, camHeight) camPos = (Point3(0.0, -9.0 * heightScaleFactor, camHeight), defLookAt, Point3(0.0, camHeight, camHeight * 4.0), Point3(0.0, camHeight, camHeight * -1.0), 0) self.smartCamera.initializeSmartCamera() self.smartCamera.setIdealCameraPos(camPos[0]) self.smartCamera.setLookAtPoint(defLookAt) def setDNAStrand(self, dnaStrand): DistributedToon.setDNAStrand(self, dnaStrand) self.initCollisions() self.setupCamera() def setMoney(self, money): DistributedToon.setMoney(self, money) self.moneyGui.update(money) def setupNameTag(self, tempName = None): DistributedToon.setupNameTag(self, tempName) if self.nameTag: self.nameTag.setColorLocal() def d_broadcastPositionNow(self): self.d_clearSmoothing() self.d_broadcastPosHpr() def b_setAnimState(self, anim, callback = None, extraArgs = []): if self.anim != anim: self.d_setAnimState(anim) DistributedToon.setAnimState(self, anim, callback=callback, extraArgs=extraArgs) def attachCamera(self): camera.reparentTo(self) camera.setPos(self.smartCamera.getIdealCameraPos()) def startSmartCamera(self): self.smartCamera.startUpdateSmartCamera() def resetSmartCamera(self): self.stopSmartCamera() self.startSmartCamera() def stopSmartCamera(self): self.smartCamera.stopUpdateSmartCamera() def detachCamera(self): camera.reparentTo(render) camera.setPos(0, 0, 0) camera.setHpr(0, 0, 0) def handleSuitAttack(self, attack_id, suit_id): DistributedToon.handleSuitAttack(self, attack_id, suit_id) if not self.isDead() and base.config.GetBool('want-sa-reactions'): base.taskMgr.remove('LT.attackReactionDone') attack = SuitAttacks.SuitAttackLengths.keys()[attack_id] suit = self.cr.doId2do.get(suit_id) animToPlay = None timeToWait = 3.0 if attack not in ('pickpocket', 'fountainpen'): suitH = suit.getH(render) % 360 myH = self.getH(render) % 360 if -90.0 <= suitH - myH <= 90.0: animToPlay = 'fallFWD' else: animToPlay = 'fallBCK' elif attack in ('pickpocket',): animToPlay = 'cringe' elif attack in ('fountainpen',): animToPlay = 'conked' timeToWait = 5.0 self.cr.playGame.getPlace().fsm.request('stop') self.b_setAnimState(animToPlay) base.taskMgr.doMethodLater(timeToWait, self.__attackReactionDone, 'LT.attackReactionDone') return def __attackReactionDone(self, task): self.cr.playGame.hood.loader.place.fsm.request('walk') self.b_setAnimState('neutral') return Task.done def enableAvatarControls(self): self.walkControls.enableAvatarControls() self.accept('control', self.updateMovementKeymap, ['jump', 1]) self.accept('control-up', self.updateMovementKeymap, ['jump', 0]) taskMgr.add(self.movementTask, 'avatarMovementTask') self.avatarMovementEnabled = True self.playMovementSfx(None) return def disableAvatarControls(self): self.walkControls.disableAvatarControls() self.ignore('arrow_up') self.ignore('arrow_up-up') self.ignore('arrow_down') self.ignore('arrow_down-up') self.ignore('arrow_left') self.ignore('arrow_left-up') self.ignore('arrow_right') self.ignore('arrow_right-up') self.ignore('control') self.ignore('control-up') taskMgr.remove('avatarMovementTask') self.isMoving_forward = False self.isMoving_side = False self.isMoving_back = False self.isMoving_jump = False self.avatarMovementEnabled = False self.playMovementSfx(None) for k, _ in self.movementKeymap.items(): self.updateMovementKeymap(k, 0) return def updateMovementKeymap(self, key, value): self.movementKeymap[key] = value def getMovementKeyValue(self, key): return self.movementKeymap[key] def playMovementSfx(self, movement): if movement == 'run': self.walkSfx.stop() self.runSfx.play() elif movement == 'walk': self.runSfx.stop() self.walkSfx.play() else: self.runSfx.stop() self.walkSfx.stop() def __forward(self): self.resetHeadHpr() self.stopLookAround() if self.getHealth() < 1: self.playMovementSfx('walk') self.setPlayRate(1.2, 'dwalk') self.setAnimState('deadWalk') else: self.playMovementSfx('run') self.setAnimState('run') self.isMoving_side = False self.isMoving_back = False self.isMoving_forward = True self.isMoving_jump = False def __turn(self): self.resetHeadHpr() self.stopLookAround() self.playMovementSfx('walk') if self.getHealth() < 1: self.setPlayRate(1.2, 'dwalk') self.setAnimState('deadWalk') else: self.setPlayRate(1.0, 'walk') self.setAnimState('walk') self.isMoving_forward = False self.isMoving_back = False self.isMoving_side = True self.isMoving_jump = False def __reverse(self): self.resetHeadHpr() self.stopLookAround() self.playMovementSfx('walk') if self.getHealth() < 1: self.setPlayRate(-1.0, 'dwalk') self.setAnimState('deadWalk') else: self.setAnimState('walkBack') self.isMoving_side = False self.isMoving_forward = False self.isMoving_back = True self.isMoving_jump = False def __jump(self): self.playMovementSfx(None) if base.localAvatar.getHealth() > 0: if self.playingAnim == 'run' or self.playingAnim == 'walk': self.b_setAnimState('leap') else: self.b_setAnimState('jump') self.isMoving_side = False self.isMoving_forward = False self.isMoving_back = False self.isMoving_jump = True return def __neutral(self): self.resetHeadHpr() self.startLookAround() self.playMovementSfx(None) if base.localAvatar.getHealth() > 0: self.setAnimState('neutral') else: self.setPlayRate(1.0, 'dneutral') self.setAnimState('deadNeutral') self.isMoving_side = False self.isMoving_forward = False self.isMoving_back = False self.isMoving_jump = False return def movementTask(self, task): if self.getMovementKeyValue('jump') == 1: if not self.walkControls.isAirborne: if self.walkControls.mayJump: self.__jump() self.hasDoneJump = True elif self.hasDoneJump: if self.getHealth() > 0: self.b_setAnimState('Happy') self.hasDoneJump = False elif not self.walkControls.isAirborne: if self.hasDoneJump: if self.getHealth() > 0: self.b_setAnimState('Happy') self.hasDoneJump = False return task.cont def startTrackAnimToSpeed(self): base.taskMgr.add(self.trackAnimToSpeed, self.uniqueName('trackAnimToSpeed')) def stopTrackAnimToSpeed(self): base.taskMgr.remove(self.uniqueName('trackAnimToSpeed')) def trackAnimToSpeed(self, task): speed, rotSpeed, slideSpeed = self.walkControls.getSpeeds() state = None if self.getHealth() > 0: state = 'Happy' else: state = 'Sad' if state != self.lastState: self.lastState = state self.b_setAnimState(state) if state == 'Sad': self.setWalkSpeedSlow() else: self.setWalkSpeedNormal() action = self.setSpeed(speed, rotSpeed) if action != self.lastAction: self.lastAction = action if action == CIGlobals.WALK_INDEX or action == CIGlobals.REVERSE_INDEX: self.resetHeadHpr() self.stopLookAround() self.playMovementSfx('walk') elif action == CIGlobals.RUN_INDEX: self.resetHeadHpr() self.stopLookAround() self.playMovementSfx('run') else: self.resetHeadHpr() self.startLookAround() self.playMovementSfx(None) return task.cont def createLaffMeter(self): r, g, b, _ = self.getHeadColor() animal = self.getAnimal() maxHp = self.getMaxHealth() hp = self.getHealth() self.laffMeter.generate(r, g, b, animal, maxHP=maxHp, initialHP=hp) self.laffMeter.start() def disableLaffMeter(self): self.laffMeter.stop() self.laffMeter.disable() def deleteLaffMeter(self): self.laffMeter.delete() def setLoadout(self, gagIds): DistributedToon.setLoadout(self, gagIds) if base.cr.playGame.getPlace() and base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'shtickerBook': if hasattr(base.cr.playGame.getPlace(), 'shtickerBookStateData'): if base.cr.playGame.getPlace().shtickerBookStateData.fsm.getCurrentState().getName() == 'inventoryPage': base.cr.playGame.getPlace().shtickerBookStateData.gui.fsm.request('idle') def enablePies(self, andKeys = 0): if self.avatarMovementEnabled and andKeys: self.enablePieKeys() self.backpack = DistributedToon.getBackpack(self) self.invGui = InventoryGui() self.invGui.createGui() self.invGui.setBackpack(self.backpack) for gag in self.backpack.getGags(): gag.setAvatar(self) self.backpack.setGagGUI(self.invGui) if self.backpack.getCurrentGag(): self.invGui.setWeapon(self.backpack.getCurrentGag().getName(), playSound=False) def enablePieKeys(self): if self.pieThrowBtn: if not self.backpack: self.backpack = DistributedToon.getBackpack(self) self.pieThrowBtn.bind(DGG.B1PRESS, self.startGag) self.pieThrowBtn.bind(DGG.B1RELEASE, self.throwGag) self.accept('delete', self.startGag) self.accept('delete-up', self.throwGag) def disablePieKeys(self): if self.pieThrowBtn: self.pieThrowBtn.unbind(DGG.B1PRESS) self.pieThrowBtn.unbind(DGG.B1RELEASE) self.ignore('delete') self.ignore('delete-up') def disablePies(self): self.disablePieKeys() self.invGui.deleteGui() if hasattr(self, 'backpack'): if self.backpack: self.backpack.setCurrentGag(None) return def setWeaponType(self, weaponType): enableKeysAgain = 0 if weaponType != self.weaponType: enableKeysAgain = 1 self.weaponType = weaponType if enableKeysAgain: self.disablePieKeys() self.enablePieKeys() def createMoney(self): self.moneyGui.createGui() self.moneyGui.update(self.money) def handleMoneyChanged(self): self.moneyGui.update() def disableMoney(self): self.moneyGui.deleteGui() def resetHeadHpr(self): self.b_lookAtObject(0, 0, 0, blink=0) def startGag(self, start = True): if not self.backpack or not self.backpack.getCurrentGag(): return if self.backpack.getSupply() > 0: if self.pieThrowBtn: self.pieThrowBtn.unbind(DGG.B1PRESS) if self.backpack.getActiveGag(): if self.backpack.getActiveGag().getState() != GagState.LOADED: return self.ignore('delete') self.backpack.getCurrentGag().setAvatar(self) self.resetHeadHpr() self.b_gagStart(self.backpack.getCurrentGag().getID()) def throwGag(self, start = True): if not self.backpack or not self.backpack.getCurrentGag() or not self.backpack.getActiveGag(): return if self.backpack.getSupply() > 0: if self.pieThrowBtn: self.pieThrowBtn.unbind(DGG.B1RELEASE) self.ignore('delete-up') if self.backpack.getActiveGag().getType() == GagType.SQUIRT and self.backpack.getActiveGag().getName() == CIGlobals.SeltzerBottle: self.b_gagRelease(self.backpack.getActiveGag().getID()) else: self.b_gagThrow(self.backpack.getActiveGag().getID()) activeGag = self.backpack.getActiveGag() if not activeGag: activeGag = self.backpack.getCurrentGag() if not activeGag.doesAutoRelease(): Sequence(Wait(0.75), Func(self.releaseGag), Wait(0.3), Func(self.enablePieKeys)).start() def releaseGag(self): if not self.backpack or not self.backpack.getActiveGag(): return if self.backpack.getSupply() > 0: gag = self.backpack.getActiveGag() if not gag: gag = self.backpack.getCurrentGag() if gag.getState() != GagState.RELEASED: gagName = gag.getName() self.b_gagRelease(GagGlobals.getIDByName(gagName)) def checkSuitHealth(self, suit): pass def handleLookSpot(self, hpr): h, p, r = hpr self.d_lookAtObject(h, p, r, blink=1) def showPieButton(self): geom = CIGlobals.getDefaultBtnGeom() self.pieThrowBtn = DirectButton(geom=geom, geom_scale=(0.75, 1, 1), text='Throw Gag', text_scale=0.05, text_pos=(0, -0.01), relief=None, parent=base.a2dTopCenter, pos=(0, 0, -0.1)) self.pieThrowBtn.setBin('gui-popup', 60) return def hidePieButton(self): self.pieThrowBtn.removeNode() self.pieThrowBtn = None return def showBookButton(self, inBook = 0): self.book_gui = loader.loadModel('phase_3.5/models/gui/sticker_open_close_gui.bam') self.book_btn = DirectButton(geom=(self.book_gui.find('**/BookIcon_CLSD'), self.book_gui.find('**/BookIcon_OPEN'), self.book_gui.find('**/BookIcon_RLVR')), relief=None, pos=(-0.175, 0, 0.163), command=self.bookButtonClicked, scale=(0.7, 0.8, 0.8), parent=base.a2dBottomRight) self.book_btn.setBin('gui-popup', 60) if inBook: self.book_btn['geom'] = (self.book_gui.find('**/BookIcon_OPEN'), self.book_gui.find('**/BookIcon_CLSD'), self.book_gui.find('**/BookIcon_RLVR2')) self.book_btn['command'] = self.bookButtonClicked self.book_btn['extraArgs'] = [0] return def hideBookButton(self): if hasattr(self, 'book_gui'): self.book_gui.removeNode() del self.book_gui if hasattr(self, 'book_btn'): self.book_btn.destroy() del self.book_btn def bookButtonClicked(self, openIt = 1): if openIt: base.cr.playGame.getPlace().fsm.request('shtickerBook') else: base.cr.playGame.getPlace().shtickerBookStateData.finished('resume') def startMonitoringHP(self): taskMgr.add(self.monitorHealth, 'localToon-monitorHealth') def monitorHealth(self, task): if self.isDead(): base.taskMgr.remove('LT.attackReactionDone') if self.cr.playGame.hood.id != ZoneUtil.getHoodId(self.zoneId): self.cr.playGame.getPlace().fsm.request('died', [{}, self.diedStateDone]) return task.done return task.cont def stopMonitoringHP(self): taskMgr.remove('localToon-monitorHealth') def setHealth(self, hp): if hp > 0 and self.getHealth() < 1: if self.cr.playGame.getPlace(): if self.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'walk': if self.cr.playGame.getPlace().walkStateData.fsm.getCurrentState().getName() == 'deadWalking': self.cr.playGame.getPlace().walkStateData.fsm.request('walking') if self.animFSM.getCurrentState().getName() == 'deadNeutral': self.playMovementSfx(None) self.b_setAnimState('neutral') elif self.animFSM.getCurrentState().getName() == 'deadWalk': self.playMovementSfx('run') self.b_setAnimState('run') DistributedToon.setHealth(self, hp) return def diedStateDone(self, requestStatus): hood = self.cr.playGame.hood.id if hood == CIGlobals.BattleTTC: hood = CIGlobals.ToontownCentral toZone = ZoneUtil.getZoneId(hood) if self.zoneId != toZone: requestStatus = {'zoneId': toZone, 'hoodId': hood, 'where': ZoneUtil.getWhereName(toZone), 'avId': self.doId, 'loader': ZoneUtil.getLoaderName(toZone), 'shardId': None, 'wantLaffMeter': 1, 'how': 'teleportIn'} self.cr.playGame.getPlace().doneStatus = requestStatus messenger.send(self.cr.playGame.getPlace().doneEvent) else: return return def teleportToCT(self): toZone = CIGlobals.CogTropolisId hood = CIGlobals.CogTropolis requestStatus = {'zoneId': toZone, 'hoodId': hood, 'where': ZoneUtil.getWhereName(toZone), 'avId': self.doId, 'loader': ZoneUtil.getLoaderName(toZone), 'shardId': None, 'wantLaffMeter': 1, 'how': 'teleportIn'} self.cr.playGame.getPlace().fsm.request('teleportOut', [requestStatus]) return def createChatInput(self): self.chatInput.load() self.chatInput.enter() def disableChatInput(self): self.chatInput.exit() self.chatInput.unload() def collisionsOn(self): self.controlManager.collisionsOn() def collisionsOff(self): self.controlManager.collisionsOff() def generate(self): DistributedToon.generate(self) def delete(self): DistributedToon.delete(self) self.deleteLaffMeter() def disable(self): base.camLens.setMinFov(CIGlobals.OriginalCameraFov / (4.0 / 3.0)) self.friendsList.destroy() self.friendsList = None self.positionExaminer.delete() self.positionExaminer = None self.disablePicking() self.stopMonitoringHP() taskMgr.remove('resetHeadColorAfterFountainPen') taskMgr.remove('LT.attackReactionDone') self.stopLookAround() DistributedToon.disable(self) self.disableAvatarControls() self.disableLaffMeter() self.disablePies() self.disableChatInput() self.weaponType = None self.pieType = None self.myBattle = None self.ignore('gotLookSpot') return def announceGenerate(self): DistributedToon.announceGenerate(self) self.setupPicker() self.setupControls() self.startLookAround() self.friendRequestManager.watch() self.accept('gotLookSpot', self.handleLookSpot) def printAvPos(self): print 'Pos: %s, Hpr: %s' % (self.getPos(), self.getHpr())
class Life(ShowBase): def __init__(self): ShowBase.__init__(self) base.disableMouse() base.setFrameRateMeter(True) mydir = os.path.abspath(sys.path[0]) mydir = Filename.fromOsSpecific(mydir).getFullpath() self.bgmusic = self.loader.loadMusic(mydir + '/../sounds/bgmusic.ogg') self.bgmusic.play() # Setup collision for 3d picking self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() # Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be positioned # relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other # collision we could separate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) # Configure boxes and textures self.box = [[None for x in range(CELL_WIDTH)] for x in range(CELL_HEIGHT)] self.textureempty = self.loader.loadTexture(mydir + '/../textures/box.png') self.texturefull = self.loader.loadTexture(mydir + '/../textures/boxfull.png') self.textureempty.setMagfilter(Texture.FTLinear) self.textureempty.setMinfilter(Texture.FTLinearMipmapLinear) self.texturefull.setMagfilter(Texture.FTLinear) self.texturefull.setMinfilter(Texture.FTLinearMipmapLinear) self.worldnode = render.attachNewNode('worldnode') self.boxnode = self.worldnode.attachNewNode('boxnode') self.worldnode.setPos(0, 200, 0) for row in range(CELL_HEIGHT): for col in range(CELL_WIDTH): box = self.loader.loadModel(mydir + '/../models/cube') box.reparentTo(self.boxnode) box.setPos((CELL_WIDTH * -1) + (col * 2), 0, CELL_HEIGHT - (row * 2)) box.setTexture(self.textureempty) # Cube is the name of the polygon set in blender box.find("**/Cube").node().setIntoCollideMask(BitMask32.bit(1)) box.find("**/Cube").node().setTag('square', str(row) + '-' + str(col)) self.box[row][col] = box # Configure cell data self.cells = [[0 for x in range(CELL_WIDTH)] for x in range(CELL_HEIGHT)] self.cells[3][6] = 1 self.cells[4][7] = 1 self.cells[5][5] = 1 self.cells[5][6] = 1 self.cells[5][7] = 1 self.editmode = False taskMgr.add(self.start, 'start') # Setup initial event handling self.accept('escape', sys.exit) self.accept('enter', self.startgame) self.accept('mouse1', self.startgame) # Prep the main screen self.boxnode.setPosHpr(self.worldnode, -5, -160, 0, 60, -25, 0) self.readyText = OnscreenText(text='Life', pos=(0.91, 0.7), scale=0.2, fg=(255, 255, 255, 255), shadow=(0, 0, 0, 100)) def mouserotation(self, task): if not self.editmode and base.mouseWatcherNode.hasMouse(): self.boxnode.setH(self.worldnode, base.mouseWatcherNode.getMouseX() * 60) self.boxnode.setP(self.worldnode, -base.mouseWatcherNode.getMouseY() * 60) return task.cont def startgame(self): # Transition to the game start state taskMgr.add(self.transition, 'transition') interval = LerpPosHprInterval(self.boxnode, TRANSITIONPERIOD, Point3(0, 0, 0), Point3(0, 0, 0), other=self.worldnode, blendType='easeInOut') interval.start() def transition(self, task): self.ignore('enter') self.ignore('mouse1') self.readyText.setFg((255, 255, 255, (max(0, TRANSITIONPERIOD - task.time) / TRANSITIONPERIOD))) self.readyText.setShadow((0, 0, 0, (max(0, TRANSITIONPERIOD - task.time) / TRANSITIONPERIOD))) if task.time > TRANSITIONPERIOD: self.accept('enter', self.handleenter) self.accept('mouse1', self.selectpiece) taskMgr.add(self.mouserotation, 'mouserotation') return task.done else: return task.cont def selectpiece(self): if self.editmode: mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.boxnode) if self.pq.getNumEntries() > 0: # If we have hit something, sort the hits so that the closest # is first, and highlight that node self.pq.sortEntries() tag = self.pq.getEntry(0).getIntoNode().getTag('square') tagsplit = tag.split('-') row = int(tagsplit[0]) col = int(tagsplit[1]) # Set the highlight on the picked square self.cells[row][col] = (0 if self.cells[row][col] == 1 else 1) def start(self, task): if not self.editmode: self.processcells(self.cells) for row in range(CELL_HEIGHT): for col in range(CELL_WIDTH): if self.cells[row][col] == 1: self.box[row][col].setTexture(self.texturefull) else: self.box[row][col].setTexture(self.textureempty) return task.cont def handleenter(self): self.editmode = not self.editmode @staticmethod def countsiblingcells(cells, x, y): return cells[y - 1][x - 1] + \ cells[y][x - 1] + \ cells[(y + 1) % CELL_HEIGHT][x - 1] + \ cells[y - 1][x] + \ cells[(y + 1) % CELL_HEIGHT][x] + \ cells[y - 1][(x + 1) % CELL_WIDTH] + \ cells[y][(x + 1) % CELL_WIDTH] + \ cells[(y + 1) % CELL_HEIGHT][(x + 1) % CELL_WIDTH] def processcells(self, cells): newcells = copy.deepcopy(cells) for row in range(CELL_HEIGHT): for col in range(CELL_WIDTH): neighbours = self.countsiblingcells(newcells, col, row) if newcells[row][col] == 1: if neighbours < 2: cells[row][col] = 0 elif 2 <= neighbours <= 3: pass elif neighbours > 3: cells[row][col] = 0 else: if neighbours == 3: cells[row][col] = 1
class MyApp(ShowBase): def __init__(self): ShowBase.__init__(self) # Create a traverser and a handler self.cTrav = CollisionTraverser() self.cQueue = CollisionHandlerQueue() # Create the collision node that will store the collision # ray solid self.pickerNode = CollisionNode('mouseRay') # Set bitmask for efficiency, only hit from objects with same mask self.pickerNode.setFromCollideMask(BitMask32.bit(1)) # Attach collision node to camera, since that is the source self.pickerNP = camera.attachNewNode(self.pickerNode) # Add collision solid(ray) to collision node self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) # Add collidable node(pickerNP) to the traverser # Collisions detected when traversed will go to cQueue self.cTrav.addCollider(self.pickerNP, self.cQueue) # Create visible sphere self.tmpSphere = self.loader.loadModel("models/misc/sphere") self.tmpSphere.reparentTo(self.render) self.tmpSphere.setColor(1, 1, 1, 1) self.tmpSphere.setPos(0, 100, 0) # Create collision sphere and attach to tmpSphere cSphere = CollisionSphere(0, 0, 0, 3) cnodePath = self.tmpSphere.attachNewNode(CollisionNode('cnode')) # Add collision solid(sphere) to collision node # Because tmpSphere/cSphere is child of render, which we traverse # later, it becomes a from collider automatically, we don't # need to addCollider since that is only for from collision nodes cnodePath.node().addSolid(cSphere) # Set bitmask to match the from collisionnode mask for efficiency cnodePath.setCollideMask(BitMask32.bit(1)) # Show the collision sphere visibly, for debugging. cnodePath.show() # Set a custom tag on the collision node which becomes available # inside the collision event stored in the collision handler cnodePath.setTag('someTag', '1') # Add task to run every frame - set collision solid(ray) # to start at the current camera position, self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') def mouseTask(self, task): if self.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() # Set collision ray to start at camera lens and endpoint at mouse self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) # Perform the collision traverse - follows the ray and logs # all collisions to the handler set earlier self.cTrav.traverse(self.render) # Get info from entries and use them if self.cQueue.getNumEntries() > 0: self.cQueue.sortEntries() print("Collision!") entry = self.cQueue.getEntry(0) print(entry) someTag = int(entry.getIntoNode().getTag('someTag')) print(someTag) # Collision detected, mouse must be over sphere self.tmpSphere.setColor(0,1,0,1) else: # No collisions, mouse is not over sphere, unset color # Not ideal to "unset" the color each frame self.tmpSphere.setColor(1,1,1,1) return task.cont
class MouseHandler(object): def __init__(self,pandaScene): self.pandaScene = pandaScene def setupMouseCollisionWM(self): self.mouseTraverser = CollisionTraverser() self.mouseCollisionQueue = CollisionHandlerQueue() self.mouseRay = CollisionRay() self.mouseRay.setOrigin(self.pandaScene.worldMapCam.getPos(self.pandaScene.render)) self.mouseRay.setDirection(self.pandaScene.render.getRelativeVector(self.pandaScene.worldMapCam, Vec3(0,1,0))) self.mouseNode = CollisionNode('mouseRay') self.mouseNode.addSolid(self.mouseRay) self.mouseNodePath = self.pandaScene.worldMapCam.attachNewNode(self.mouseNode) self.mouseNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.mouseTraverser.addCollider(self.mouseNodePath, self.mouseCollisionQueue) # uncomment to see the collisions # self.mouseTraverser.showCollisions(self.pandaScene.render) def selectedObjectIdWM(self): if (self.pandaScene.worldMapMouse.hasMouse() == False): return 0,None mpos = self.pandaScene.worldMapMouse.getMouse() self.mouseRay.setFromLens(self.pandaScene.camNode, mpos.getX(), mpos.getY()) self.mouseTraverser.traverse(self.pandaScene.render) if (self.mouseCollisionQueue.getNumEntries() > 0): self.mouseCollisionQueue.sortEntries() entry = self.mouseCollisionQueue.getEntry(0); selectedObj = entry.getIntoNodePath() selectedObj = selectedObj.findNetTag('selectable') if not selectedObj.isEmpty(): return selectedObj.getTag('id'),selectedObj else: return 0,None return 0,None def selectedObjectId(self): if (self.pandaScene.mouseWatcherNode.hasMouse() == False): return 0,None mpos = base.mouseWatcherNode.getMouse() self.mouseRay.setFromLens(self.pandaScene.camNode, mpos.getX(), mpos.getY()) self.mouseTraverser.traverse(self.pandaScene.render) if (self.mouseCollisionQueue.getNumEntries() > 0): self.mouseCollisionQueue.sortEntries() entry = self.mouseCollisionQueue.getEntry(0); selectedObj = entry.getIntoNodePath() selectedObj = selectedObj.findNetTag('selectable') if not selectedObj.isEmpty(): return selectedObj.getTag('id'),selectedObj else: return 0,None return 0,None def setupMouseCollision(self): self.mouseTraverser = CollisionTraverser() self.mouseCollisionQueue = CollisionHandlerQueue() self.mouseRay = CollisionRay() self.mouseRay.setOrigin(self.pandaScene.camera.getPos(self.pandaScene.render)) self.mouseRay.setDirection(self.pandaScene.render.getRelativeVector(self.pandaScene.camera, Vec3(0,1,0))) self.mouseNode = CollisionNode('mouseRay') self.mouseNode.addSolid(self.mouseRay) self.mouseNodePath = self.pandaScene.camera.attachNewNode(self.mouseNode) self.mouseNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.mouseTraverser.addCollider(self.mouseNodePath, self.mouseCollisionQueue)
class World(ShowBase): def __init__(self): ShowBase.__init__(self) #PStatClient.connect() #self.lookPoint = NodePath(PandaNode("floater")) self.lookPoint = self.loader.loadModel("models/cone") self.lookPoint.reparentTo(render) self.menu = StartMenu(self) #self.bar = Bar() #self.messenger.toggleVerbose() #sys.exit() #pdb.set_trace() # Window change event handler #self.windowEventSetup() def setup(self): print("Init Levels...") self.initLevels() print("Init World...") self.initWorld("yard") print("Init Items...") self.initItems() print("Init Actors...") self.initActors() print("Init GUI ...") self.initGui() print("Init Lights...") self.initLights() print("Init Collisions...") self.initCollision() print("Init Tasks...") self.initTasks() print("Launching World") # Accept the control keys #self.accept("h", self.crono.start) def initItems(self): with open('items/items.json') as items_file: self.items = json.load(items_file) def initActors(self): self.player = Player(self, 20, 10, 10, 10, 10, 10) #app, hp, mana, strength, dexterity, vigor, magic): self.enemies = [] self.npcs = [] #Creating AI World self.AIworld = AIWorld(render) """self.foe1 = Enemy(self, 100, 50, 5, 2, "bug") #(self, app, hp, mana, speed, attackSpeed, name): self.nasgul = Enemy(self, 100, 50, 5, 2, "nasgul") self.npc1 = Npc(self, 100, 50, 5, 2, "guy2") self.npc2 = Npc(self, 100, 50, 5, 2, "ralph") self.enemies.append(self.foe1) self.enemies.append(self.nasgul) self.npcs.append(self.npc1) self.npcs.append(self.npc2)""" def initGui(self): # Load the models. self.inventory = Inventory(self) self.status = Status(self) self.skills = Skills(self) #self.statusBar = self.loader.loadModel("models/statusbar") ##self.statusBar.setDepthTest(True) #self.statusBar.setDepthWrite(True) # Reparent the model to render2d. #self.statusBar.reparentTo(self.render2d) #self.statusBar.setScale(0.15, 0.15, 0.15) #self.statusBar.setPos(-0.95, 0, 0.65) #self.crono = Crono(self) ##self.cursorpos = CursorPos(self) #self.playerpos = PlayerPos(self) #self.crono.draw(0.7, -0.85) #self.cursorpos.draw(0.0, -0.85) #self.playerpos.draw(-0.7, -0.85) self.accept("i", self.inventory.toggle) self.accept("c", self.status.toggle) self.accept("k", self.skills.toggle) def initTasks(self): #self.taskMgr.add(self.crono.task, "cronoTask") #self.taskMgr.add(self.cursorpos.task, "cursorposTask") ###self.taskMgr.add(self.playerpos.task, "playerposTask") self.taskMgr.add(self.checkCollision, "collisionTask") #self.taskMgr.add(self.player.update, "updateTask") self.taskMgr.add(self.player.move, "moveTask") self.taskMgr.add(self.player.updateCamera, "playerCameraTask",priority=1) """ #self.taskMgr.add(self.foe1.update, "bugTask",priority=1) #self.taskMgr.add(self.nasgul.update, "nasgulTask",priority=1) #self.taskMgr.add(self.npc1.update, "npc1Task",priority=1) #self.taskMgr.add(self.npc2.update, "npc2Task",priority=1) """ self.taskMgr.add(self.update, 'update') def initLights(self): # Create some lighting #self.environ.ls() #print(self.environ.findAllMatches("**/Spot")) ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(0.8, 0.8, 0.8, 0.65)) """ directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(-10, -10, 5)) directionalLight.showFrustum() directionalLight.setColor(Vec4(1, 1, 1, 1)) directionalLight.setSpecularColor(Vec4(1, 1, 1, 1)) dirnp = render.attachNewNode(directionalLight) dirnp.setPos(10, 0, 6) """ plight1 = PointLight('plight1') plight1.setColor(VBase4(1, 1, 1, 1)) plight1.showFrustum() #plight1.setShadowCaster(True) plnp1 = render.attachNewNode(plight1) plnp1.setPos(26.71, -33.2, 26) plight2 = PointLight('plight2') plight2.setColor(VBase4(1, 1, 1, 1)) plight2.showFrustum() plnp2 = render.attachNewNode(plight2) plnp2.setPos(-25, 25, 25) slight = Spotlight('slight') slight.setColor(VBase4(1, 1, 1, 1)) lens = PerspectiveLens() lens.setFilmSize(1, 1) # Or whatever is appropriate for your scene slight.setLens(lens) slight.setShadowCaster(True, 512, 512) slight.showFrustum() slnp = render.attachNewNode(slight) slnp.setPos(0, 0, 100) slnp.lookAt(Vec3(0,0,0)) render.setLight(slnp) render.setLight(plnp1) render.setLight(plnp2) #render.setLight(render.attachNewNode(ambientLight)) #render.setLight(dirnp) render.setShaderAuto() #render.setLight(render.attachNewNode(directionalLight)) """ self.light = render.attachNewNode(Spotlight("Spot")) self.light.node().setScene(render) self.light.node().setShadowCaster(True) self.light.node().showFrustum() self.light.node().getLens().setFov(40) self.light.node().getLens().setNearFar(10,100) render.setLight(self.light) """ def initLevels(self): with open('levels/levels.json') as levels_file: self.levels = json.load(levels_file) def initWorld(self, level): self.environ = self.loader.loadModel(self.levels["levels"][level]["model"]) #self.environ.setScale(20, 20, 20) #self.environ.setHpr(0, 0, 0) self.environ.setPos(0, 0, 0) self.playerStartPos = self.environ.find("**/startPos").getPos() # Reparent the model to render self.environ.reparentTo(render) #self.environ.ls() self.accept("q", self.changeMap) def destroyWorld(self): self.environ.detachNode() self.environ.removeNode() def changeMap(self):#, levelName): self.destroyWorld() self.initWorld("yard") def update(self, task): dt = globalClock.getDt() self.AIworld.update() return task.cont def setKey(self, key, value): self.keyMap[key] = value def windowEventSetup( self ): # accept the window event's self.accept( 'window-event', self.windowEventHandler) # create a window event yourself #messenger.send( 'window-event', [self.win] ) def windowEventHandler( self, window=None ): wp = window.getProperties() print("Window changed") print("X = %s" % wp.getXSize()) print("Y = %s" % wp.getYSize()) self.setResolution( wp.getXSize(), wp.getYSize() ) def setResolution( self, w, h ): wp = WindowProperties() wp.setSize( w, h ) if os.name == 'posix': self.openMainWindow() self.graphicsEngine.openWindows() self.win.requestProperties( wp ) # Define a procedure to move the camera. def spinCameraTask(self, task): angleDegrees = task.time * 6.0 angleRadians = angleDegrees * (pi / 180.0) self.camera.setPos(40 * sin(angleRadians), -10.0 * cos(angleRadians), 3) self.camera.setHpr(angleDegrees, 0, 0) return Task.cont def initCollision(self): self.enemyGroundRay = [] self.enemyGroundCol = [] self.enemyGroundColNp = [] self.enemyGroundHandler = [] self.npcGroundRay = [] self.npcGroundCol = [] self.npcGroundColNp = [] self.npcGroundHandler = [] self.cTrav = CollisionTraverser() self.playerGroundRay = CollisionRay() self.playerGroundRay.setOrigin(0, 0, 9) self.playerGroundRay.setDirection(0, 0, -1) self.playerGroundCol = CollisionNode('playerRay') self.playerGroundCol.addSolid(self.playerGroundRay) self.playerGroundCol.setFromCollideMask(CollideMask.bit(0)) self.playerGroundCol.setIntoCollideMask(CollideMask.allOff()) self.playerGroundColNp = self.player.moveFloater.attachNewNode(self.playerGroundCol) self.playerGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.playerGroundColNp, self.playerGroundHandler) self.mouseGroundRay = CollisionRay() self.mouseGroundRay.setOrigin(0, 0, 9) self.mouseGroundRay.setDirection(0, 0, -1) self.mouseGroundCol = CollisionNode('mouseRay') self.mouseGroundCol.addSolid(self.mouseGroundRay) self.mouseGroundCol.setFromCollideMask(CollideMask.bit(0)) self.mouseGroundCol.setIntoCollideMask(CollideMask.allOff()) self.mouseGroundColNp = self.camera.attachNewNode(self.mouseGroundCol) self.mouseGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.mouseGroundColNp, self.mouseGroundHandler) # Uncomment this line to see the collision rays #self.playerGroundColNp.show() #self.mouseGroundColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) i = 0 for enemy in self.enemies: self.enemyGroundRay.append(CollisionRay()) self.enemyGroundRay[i].setOrigin(0, 0, 9) self.enemyGroundRay[i].setDirection(0, 0, -1) self.enemyGroundCol.append(CollisionNode('%sRay' % enemy.name)) self.enemyGroundCol[i].addSolid(self.enemyGroundRay[i]) self.enemyGroundCol[i].setFromCollideMask(CollideMask.bit(0)) self.enemyGroundCol[i].setIntoCollideMask(CollideMask.allOff()) self.enemyGroundColNp.append(enemy.enemyActor.attachNewNode(self.enemyGroundCol[i])) self.enemyGroundHandler.append(CollisionHandlerQueue()) self.cTrav.addCollider(self.enemyGroundColNp[i], self.enemyGroundHandler[i]) #self.enemyGroundColNp.show() i += 1 i = 0 for npc in self.npcs: self.npcGroundRay.append(CollisionRay()) self.npcGroundRay[i].setOrigin(0, 0, 9) self.npcGroundRay[i].setDirection(0, 0, -1) self.npcGroundCol.append(CollisionNode('%sRay' % npc.name)) self.npcGroundCol[i].addSolid(self.npcGroundRay[i]) self.npcGroundCol[i].setFromCollideMask(CollideMask.bit(0)) self.npcGroundCol[i].setIntoCollideMask(CollideMask.allOff()) self.npcGroundColNp.append(npc.npcActor.attachNewNode(self.npcGroundCol[i])) self.npcGroundHandler.append(CollisionHandlerQueue()) self.cTrav.addCollider(self.npcGroundColNp[i], self.npcGroundHandler[i]) #self.npcGroundColNp.show() i += 1 def checkCollision(self, task): startpos = self.player.moveFloater.getPos() entries = list(self.playerGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) for entry in entries: if entry > 0 and entries[0].getIntoNode().getName() == "Ground": self.player.moveFloater.setZ(entry.getSurfacePoint(render).getZ()) else: self.player.moveFloater.setPos(startpos) if self.mouseWatcherNode.hasMouse(): mpos = self.mouseWatcherNode.getMouse() self.mouseGroundRay.setFromLens(self.camNode, mpos.getX(), mpos.getY()) nearPoint = render.getRelativePoint(self.camera, self.mouseGroundRay.getOrigin()) nearVec = render.getRelativeVector(self.camera, self.mouseGroundRay.getDirection()) try: self.lookPoint.setPos(PointAtZ(self.player.moveFloater.getZ(), nearPoint, nearVec)) except: pass i = 0 for enemy in self.enemies: startpos = enemy.enemyActor.getPos() entries = list(self.enemyGroundHandler[i].getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) for entry in entries: if entry > 0: # and entries[0].getIntoNode().getName() == "Ground": enemy.enemyActor.setZ(entry.getSurfacePoint(render).getZ()) else: enemy.enemyActor.setPos(startpos) i += 1 i = 0 for npc in self.npcs: startpos = npc.npcActor.getPos() entries = list(self.npcGroundHandler[i].getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) for entry in entries: if entry > 0: # and entries[0].getIntoNode().getName() == "Ground": npc.npcActor.setZ(entry.getSurfacePoint(render).getZ()) else: npc.npcActor.setPos(startpos) i += 1 return task.cont
class World(DirectObject): def __init__(self): # The standard camera, light and background initialization base.setBackgroundColor(0, 0, 0) base.disableMouse() self.cam_ctrl = CameraController() self.cam_ctrl.reset() alight = AmbientLight('alight') alight.setColor((0.2, 0.2, 0.2, 1)) alnp = render.attachNewNode(alight) render.setLight(alnp) # The global variables we use to control the speed and size of objects self.yearscale = 900 self.dayscale = self.yearscale / 365.0 * 30 self.yearCounter = 0 self.dayCounter = 0 self.money = 2000 self.system_population = 0 self.orbitscale = 10 self.sizescale = 0.6 # Global game balance variables self.population_time_delta = 3 self.tax_factor = 0.1 self.salvage_factor = 0.75 self.food_consuming_factor = 0.75 self.goods_cap = 1000 self.PlanetInfoModeOn = False self.capitalPlanet = None self.galaxy_objects = [] self.BuildingsDB = {} # Will contain all buildable structures # Everything that's needed to detect selecting objects with mouse self.pickerNode = CollisionNode('mouseRay') self.pickerNP = camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.collQueue = CollisionHandlerQueue() base.cTrav = CollisionTraverser('myTraverser') base.cTrav.addCollider(self.pickerNP, self.collQueue) # Set up the start screen self.create_gui() self.NewPlanetInfoView = PlanetInfoView(self) self.NewPlanetBuildView = PlanetBuildView(self) self.load_planets() self.rotate_planets() self.BuildingsDB = buildingsDB.loadDB() self.set_capital_planet() # Add all constantly running checks to the taskmanager taskMgr.add(self.redraw_head_gui, "redrawHeadGUITask") taskMgr.doMethodLater(self.population_time_delta, self.populate_planet_task, 'populatePlanetTask', extraArgs=[self.Earth], appendTask=True) taskMgr.doMethodLater(2, self.generate_money_task, 'generateMoneyTask') # Open up all listeners for varous mouse and keyboard inputs self.accept("escape", sys.exit) self.accept('mouse1', self.handle_mouse_click) # **************************************** # Main Gameplay Functions * # **************************************** # Camera, controle and main GUI functions # ---------------------------------------- def handle_mouse_click(self): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) base.cTrav.traverse(render) if self.collQueue.getNumEntries() > 0: self.collQueue.sortEntries() pickedObj = self.collQueue.getEntry(0).getIntoNodePath() pickedObj = pickedObj.findNetTag('clickable') if not pickedObj.isEmpty() and not self.PlanetInfoModeOn: instance = pickedObj.getPythonTag('instance') self.toggle_planet_info_mode(True, instance) def redraw_head_gui(self, task): self.HeadGUIText['text'] = ('Year ' + str(self.yearCounter) + ', ' 'Day ' + str(self.dayCounter) + ', ' 'Money: ' + str(self.money) + ', ' 'Population: ' + str(self.system_population)) return task.cont # Smaller single line functions # ----------------------------- def incYear(self): self.yearCounter += 1 def incDay(self): self.dayCounter += 1 # Functions to interact with the planet info view # ------------------------------------------------ def toggle_planet_info_mode(self, mode=False, obj=None): if mode: self.MapViewPanel.hide() self.PlanetInfoModeOn = True self.NewPlanetInfoView.reset(obj) zoomInterval = Sequence(Func(self.cam_ctrl.info_view_to, obj), Wait(0.2), Func(self.NewPlanetInfoView.show)) zoomInterval.start() else: self.PlanetInfoModeOn = False self.cam_ctrl.reset() taskMgr.remove('updatePlanetInfoTask') self.NewPlanetInfoView.hide() # Global general purpose functions and tasks # --------------------------------------------------- def populate_planet_task(self, planet, task): habProblem = planet.habitation_cap <= planet.population foodProblem = (not ('Vegetable crates' in planet.goods) or planet.goods['Vegetable crates'] < planet.population) nutrition_decrease = round(planet.population * self.food_consuming_factor) if foodProblem: d = random.randint(-1, 2) if planet.population - d <= 0: planet.population = 0 else: planet.population -= d elif habProblem: pass planet.goods['Vegetable crates'] -= nutrition_decrease else: planet.population += random.randint(1, 3) planet.goods['Vegetable crates'] -= nutrition_decrease return task.again def count_system_population(self): wholePop = 0 for obj in self.galaxy_objects: if type(obj) != Star: wholePop += obj.population self.system_population = wholePop def generate_money_task(self, task): self.count_system_population() self.money += round(self.system_population * self.tax_factor) return task.again def add_message(self, planet, id, mType, text, value): planet.messages.update( {id: { 'type': mType, 'text': text, 'value': value }}) def remove_message(self, planet, id): if id in planet.messages: planet.messages.pop(id) def calc_distance_between_planets(self, planet1, planet2): pos1 = planet1.getPos() pos2 = planet2.getPos() diffX = abs(pos1[0] - pos2[0]) diffY = abs(pos1[1] - pos2[1]) dist = math.sqrt(diffX**2 + diffY**2) return dist def create_dialog(self, problemText, form='ok', function=lambda: None, args=[]): if form == 'ok': self.ProblemDialog = OkDialog(dialogName="OkDialog", text=problemText, text_pos=(0, 0.07), command=self.cleanup_dialog, midPad=(-0.15), frameColor=(0, 0, 0, 0), text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, geom=self.dialog_map, extraArgs=[function, args]) elif form == 'yesNo': self.ProblemDialog = YesNoDialog(dialogName="OkDialog", text=problemText, text_pos=(0, 0.07), command=self.cleanup_dialog, midPad=(-0.15), frameColor=(0, 0, 0, 0), text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, geom=self.dialog_map, extraArgs=[function, args]) def cleanup_dialog(self, value, function, args): self.ProblemDialog.cleanup() if value: function(*args) # **************************************** # Initialisation Functions * # **************************************** def load_planets(self): self.orbit_root_mercury = render.attachNewNode('orbit_root_mercury') self.orbit_root_venus = render.attachNewNode('orbit_root_venus') self.orbit_root_mars = render.attachNewNode('orbit_root_mars') self.orbit_root_earth = render.attachNewNode('orbit_root_earth') self.orbit_root_moon = ( self.orbit_root_earth.attachNewNode('orbit_root_moon')) self.sky = loader.loadModel("models/sky_dome.blend") self.sky_tex = loader.loadTexture("models/sky_tex2_cut.jpg") self.sky_tex.setWrapU(Texture.WM_clamp) self.sky.setTexture(self.sky_tex, 1) self.sky.reparentTo(render) self.sky.setScale(300) self.sky.setHpr(270, 0, 0) self.Sun = Star(self, 'Sun', 'models/planet_sphere', 'models/sun_1k_tex.jpg', 2) self.Mercury = Planet(self, 'Mercury', 'models/planet_sphere', 'models/mercury_1k_tex.jpg', self.orbit_root_mercury, 0.385, 0.38, False, 1, { 'Coal': 'Common', 'Iron': 'Common' }) self.Venus = Planet(self, 'Venus', 'models/planet_sphere', 'models/venus_1k_tex.jpg', self.orbit_root_venus, 0.923, 0.72, False, 2, { 'Coal': 'Common', 'Uranium': 'Normal' }) self.Mars = Planet(self, 'Mars', 'models/planet_sphere', 'models/mars_1k_tex.jpg', self.orbit_root_mars, 0.512, 1.52, False, 1, { 'Gemstone': 'Rare', 'Iron': 'Rare' }) self.Mars.probed = True self.Earth = Planet(self, 'Earth', 'models/planet_sphere', 'models/earth_1k_tex.jpg', self.orbit_root_earth, 1, 1, True, 1, { 'Iron': 'Normal', 'Coal': 'Common' }) self.orbit_root_moon.setPos(self.orbitscale, 0, 0) self.Earth_Moon = Moon(self, 'Moon', 'models/planet_sphere', 'models/moon_1k_tex.jpg', self.orbit_root_moon, 0.1, 0.1, False, 0, { 'Cheese': 'Rare', 'Coal': 'Common' }) def rotate_planets(self): self.day_period_sun = self.Sun.model.hprInterval(20, (360, 0, 0)) self.orbit_period_mercury = self.orbit_root_mercury.hprInterval( (0.241 * self.yearscale), (360, 0, 0)) self.day_period_mercury = self.Mercury.model.hprInterval( (59 * self.dayscale), (360, 0, 0)) self.orbit_period_venus = self.orbit_root_venus.hprInterval( (0.615 * self.yearscale), (360, 0, 0)) self.day_period_venus = self.Venus.model.hprInterval( (243 * self.dayscale), (360, 0, 0)) self.orbit_period_earth = Sequence( self.orbit_root_earth.hprInterval(self.yearscale, (360, 0, 0)), Func(self.incYear)) self.day_period_earth = Sequence( self.Earth.model.hprInterval(self.dayscale, (360, 0, 0)), Func(self.incDay)) self.orbit_period_moon = self.orbit_root_moon.hprInterval( (.0749 * self.yearscale), (360, 0, 0)) self.day_period_moon = self.Earth_Moon.model.hprInterval( (.0749 * self.yearscale), (360, 0, 0)) self.orbit_period_mars = self.orbit_root_mars.hprInterval( (1.881 * self.yearscale), (360, 0, 0)) self.day_period_mars = self.Mars.model.hprInterval( (1.03 * self.dayscale), (360, 0, 0)) self.day_period_sun.loop() self.orbit_period_mercury.loop() self.day_period_mercury.loop() self.orbit_period_venus.loop() self.day_period_venus.loop() self.orbit_period_earth.loop() self.day_period_earth.loop() self.orbit_period_moon.loop() self.day_period_moon.loop() self.orbit_period_mars.loop() self.day_period_mars.loop() def set_capital_planet(self): self.capitalPlanet = self.Earth self.Earth.probed = True self.Earth.colonised = True self.Earth.population = 100 self.Earth.habitation_cap = 100 def create_gui(self): self.buttonModel = loader.loadModel( 'models/gui/buttons/simple_button_maps.egg') self.buttonMaps = (self.buttonModel.find('**/normal'), self.buttonModel.find('**/active'), self.buttonModel.find('**/normal'), self.buttonModel.find('**/disabled')) self.dialog_model = loader.loadModel( 'models/gui/panels/infodialogpanel_maps.egg') self.dialog_map = self.dialog_model.find('**/infodialogpanel') self.HeadGUIPanel = DirectFrame(frameColor=(0.2, 0.2, 0.22, 0.9), frameSize=(0, 1.55, -0.13, 0), pos=(-1.8, 0, 1)) self.HeadGUIText = DirectLabel( text=('Year ' + str(self.yearCounter) + ', ' 'Day ' + str(self.dayCounter) + ', ' 'Money: ' + str(self.money) + ', ' 'Population: ' + str(self.system_population)), pos=(0.1, 0, -0.085), text_fg=(1, 1, 1, 1), frameColor=(0, 0, 0, 0), parent=self.HeadGUIPanel, text_align=TextNode.ALeft, text_scale=.07) self.MapViewPanel = DirectFrame(frameColor=(0.2, 0.2, 0.22, 0.9), frameSize=(0, 0.5, -1.25, 0), pos=(-1.75, 0, 0.6)) self.trading_button = DirectButton(text='Trading', pos=(0.25, 0, -0.12), scale=0.5, pad=(-0.1, -0.09), frameColor=(0, 0, 0, 0), parent=self.MapViewPanel, geom=(self.buttonMaps), geom_scale=(0.7, 0, 1), text_scale=0.15, text_pos=(0, -0.03), text_fg=(0.5, 0.5, 0.5, 1), state=DGG.DISABLED) self.research_button = DirectButton(text='Research', pos=(0.25, 0, -0.32), scale=0.5, pad=(-0.1, -0.09), frameColor=(0, 0, 0, 0), parent=self.MapViewPanel, geom=(self.buttonMaps), geom_scale=(0.7, 0, 1), text_scale=0.15, text_pos=(0, -0.03), text_fg=(0.5, 0.5, 0.5, 1), state=DGG.DISABLED) self.diplomacy_button = DirectButton(text='Diplomacy', pos=(0.25, 0, -0.52), scale=0.5, pad=(-0.1, -0.09), frameColor=(0, 0, 0, 0), parent=self.MapViewPanel, geom=(self.buttonMaps), geom_scale=(0.7, 0, 1), text_scale=0.14, text_pos=(0, -0.03), text_fg=(0.5, 0.5, 0.5, 1), state=DGG.DISABLED) self.military_button = DirectButton(text='Military', pos=(0.25, 0, -0.72), scale=0.5, pad=(-0.1, -0.09), frameColor=(0, 0, 0, 0), parent=self.MapViewPanel, geom=(self.buttonMaps), geom_scale=(0.7, 0, 1), text_scale=0.15, text_pos=(0, -0.03), text_fg=(0.5, 0.5, 0.5, 1), state=DGG.DISABLED) self.galacy_map_button = DirectButton(text='Galaxy Map', pos=(0.25, 0, -0.92), scale=0.5, pad=(-0.1, -0.09), frameColor=(0, 0, 0, 0), parent=self.MapViewPanel, geom=(self.buttonMaps), geom_scale=(0.7, 0, 1), text_scale=0.14, text_pos=(0, -0.03), text_fg=(0.5, 0.5, 0.5, 1), state=DGG.DISABLED) self.statistics_button = DirectButton(text='Statistics', pos=(0.25, 0, -1.12), scale=0.5, pad=(-0.1, -0.09), frameColor=(0, 0, 0, 0), parent=self.MapViewPanel, geom=(self.buttonMaps), geom_scale=(0.7, 0, 1), text_scale=0.14, text_pos=(0, -0.03), text_fg=(0.5, 0.5, 0.5, 1), state=DGG.DISABLED) # **************************************** # Debug / Testing Functions * # **************************************** def reset_game(self): self.NewPlanetInfoView.hide() self.NewPlanetBuildView.hide() self.PlanetInfoModeOn = False taskMgr.remove('quickinfoTask') taskMgr.remove('buildcamTask') taskMgr.remove('infocamTask') self.yearCounter = 0 self.dayCounter = 0 self.money = 2000 self.system_population = 0 for planet in self.galaxy_objects: planet.reset() self.set_capital_planet()
class World(DirectObject): mySound = base.loader.loadSfx("trial.mp3") #Used for adding a virus at a randomly generated position def random_vgen(self): print 'I am in random' for i in range(0,5): self.a[random.randint(0,7)][random.randint(0,7)] = 1 def initializer(self,level): self.turns = 0 ## Level Definition def levelone(): self.a[4][4] = 1 self.a[4][5] = 1 self.a[4][6] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[4][6] = 1 self.a[5][5] = 1 def leveltwo(): self.a[4][3] = 1 self.a[4][5] = 1 self.a[4][7] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[2][2] = 1 self.a[5][3] = 1 self.a[1][2] = 1 def levelthree(): self.a[4][4] = 1 self.a[4][5] = 1 self.a[4][6] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[4][6] = 1 self.a[5][5] = 1 self.a[4][3] = 1 self.a[4][5] = 1 self.a[4][7] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[2][2] = 1 self.a[5][3] = 1 options = {1: levelone, 2: leveltwo, 3: levelthree } options[level]() print self.a temp = [] count = 0 for element in reversed(self.a): for ele in element: #print 'cheking element is 1 : ' + str(ele) if ele == 1: temp.append(count) self.pieces[count] = Monster(count,WHITE) #self.squares[count].setColor(HIGHLIGHT) count = count + 1 self.list=temp def showscore(self): try: self.score.destroy() except: pass self.score = OnscreenText(text = 'Number of Turns : %s'%(self.turns), pos = (0.5, 0.95), scale = 0.07, mayChange = True) def menuscreen(self): # Callback function to set text def setText(): b.destroy() helptext.destroy() # Add button b = DirectButton(text = ("START", "START", "START", "disabled"), scale=0.15, command=setText, pos=(0,0,0.85)) helptext = OnscreenText(text =''' STORY: Hello Mad Scientist! You are so close to finding the cure to aids! You understood the behaviour and can finally manipulate the virus to kill itself! But time is running out! You have a red and white blood cell sample infected with AIDS Virus. INSTRUCTIONS: The AIDS Virus obeys the Conway's Game of Life. Who would have guessed? 1. If virus is surrounded by more than 3 viruses, it dies of suffocation 2. If virus is surrounded by less than 2 viruses, it dies of underpopulation 3. If dead empty cell, is surrounded by exactly 3 viruses, a new virus is spawned due to breeding. AIM: To kill all the viruses, by moving one of them every turn. ''', pos = (-0.90,0.72),frame = (123,123,123,1), wordwrap = 25, align = TextNode.ALeft, bg = (0.23,0.243,0.13,0.9)) def endscreen(self): def restart(): taskMgr.remove(self.mouseTask) for i in range(64): self.squares[i].setColor(SquareColor(i)) scorecard.destroy() restartb.destroy() end.destroy() nextlevelb.destroy() self.reference.destroy() self.mySound.stop() try: self.score.destroy() except: pass print 'restarting' print self.a print self.list World() def quitgame(): sys.exit() def nextLevel(): self.currlevel = self.currlevel + 1 nextlevelb.destroy() scorecard.destroy() restartb.destroy() end.destroy() self.score.destroy() self.initializer(self.currlevel) # Add button scorecard = OnscreenText(text = 'You finished it in %s turns! '%(self.turns),frame = (123,123,123,0), wordwrap = 25, bg = (0.2,0,0.8,1)) nextlevelb = DirectButton(text = ("NEXT LEVEL", "NEXT LEVEL", "NEXT LEVEL", "disabled"), scale=0.15, command=nextLevel, pos=(0,0,-0.15)) restartb = DirectButton(text = ("RESTART", "RESTART", "RESTART", "disabled"), scale=0.15, command=restart, pos=(0,0,-0.35)) end = DirectButton(text = ("QUIT", "QUIT", "QUIT", "disabled"), scale=0.15, command=quitgame, pos=(0,0,-0.55)) if self.currlevel == self.maxlevel: nextlevelb.destroy() def __init__(self): #music self.mySound.play() print 'I am being initialized' self.menuscreen() self.list = [] self.turns = 0 self.maxlevel = 3 self.currlevel = 1 self.a =[[0 for x in range(8)] for y in range(8)] #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Game of Life : Help in ending AIDS!", style=1, fg=(1,1,1,1), pos=(0,-0.95), scale = .07) self.escapeEvent = OnscreenText( text="ESC: Quit", style=1, fg=(1,1,1,1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale = .05) ## self.mouse1Event = OnscreenText( ## text="Left-click and drag: Pick up virus and drag it to a new bloodcell", ## style=1, fg=(1,1,1,1), pos=(-1.3, 0.90), ## align=TextNode.ALeft, scale = .05) ## self.accept('escape', sys.exit) #Escape quits base.disableMouse() #Disble mouse camera control camera.setPosHpr(0, -13.75, 6, 0, -25, 0) #Set the camera self.setupLights() #Setup default lighting #Since we are using collision detection to do picking, we set it up like #any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() #Make a traverser self.pq = CollisionHandlerQueue() #Make a handler #Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') #Attach that node to the camera since the ray will need to be positioned #relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() #Make our ray self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node #Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) #We will attach all of the squares to their own root. This way we can do the #collision pass just on the sqaures and save the time of checking the rest #of the scene self.squareRoot = render.attachNewNode("squareRoot") #For each square self.squares = [None for i in range(64)] self.pieces = [None for i in range(64)] for i in range(64): #Load, parent, color, and position the model (a single square polygon) self.squares[i] = loader.loadModel("models/square") self.squares[i].reparentTo(self.squareRoot) self.squares[i].setPos(SquarePos1(i)) self.squares[i].setColor(SquareColor(i)) #Set the model itself to be collideable with the ray. If this model was #any more complex than a single polygon, you should set up a collision #sphere around it instead. But for single polygons this works fine. self.squares[i].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) #Set a tag on the square's node so we can look up what square this is #later during the collision pass self.squares[i].find("**/polygon").node().setTag('square', str(i)) #We will use this variable as a pointer to whatever piece is currently #in this square self.initializer(self.currlevel) #This will represent the index of the currently highlited square self.hiSq = False #This wil represent the index of the square where currently dragged piece #was grabbed from self.dragging = False #Start the task that handles the picking self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') #self.trial = taskMgr.add(self.trial,'trial') self.accept("mouse1", self.grabPiece) #left-click grabs a piece self.accept("mouse1-up", self.releasePiece) #releasing places it #This function swaps the positions of two pieces def swapPieces(self, fr, to): temp = self.pieces[fr] self.pieces[fr] = self.pieces[to] self.pieces[to] = temp if self.pieces[fr]: print 'imma swapping' self.pieces[fr].square = fr print fr print SquarePos(fr) try: self.pieces[fr].obj.setPos(SquarePos(fr)) except: pass print 'done' if self.pieces[to]: self.pieces[to].square = to try: self.pieces[to].obj.setPos(SquarePos(to)) except: pass def trial(self): self.turns = self.turns + 1 try: self.reference.destroy() except: pass self.reference = OnscreenText( text=''' Reference: virus is surrounded by more than 3 viruses, it dies virus is surrounded by less than 2 viruses, it dies If dead empty cell, is surrounded by exactly 3 viruses, a new virus is spawned. ''' , style=1, fg=(1,1,1,1), pos=(-1, 0.95), align=TextNode.ALeft, scale = .05) self.showscore() a = self.a while True: for i in self.list: #insert deletion code print 'imma deleting the sq : ' + str(i) self.pieces[i].obj.delete() self.squares[i].setColor(SquareColor(i)) count = 0 a=[[([[sum(b[y1][x1] for b in [[[((-1<x2+dx<len(a[0])) and (-1<y2+dy<len(a))) and a[y2+dy][x2+dx] or 0 for x2 in range(len(a[0]))] for y2 in range(len(a))] for (dx,dy) in [(dx,dy) for dx in [-1,0,1] for dy in [-1,0,1] if (dy!=0 or dx!=0)]]) for x1 in range(len(a[0]))] for y1 in range(len(a))][y][x]== 3 or ([[sum(c[y3][x3] for c in [[[((-1<x4+dx<len(a[0])) and (-1<y4+dy<len(a))) and a[y4+dy][x4+dx] or 0 for x4 in range(len(a[0]))] for y4 in range(len(a))] for (dx,dy) in [(dx,dy) for dx in [-1,0,1] for dy in [-1,0,1] if (dy!=0 or dx!=0)]]) for x3 in range(len(a[0]))] for y3 in range(len(a))][y][x] == 2 and a[y][x]==1)) and 1 or 0 for x in range(len(a[0]))] for y in range(len(a))] lis = [] #insert a random virus at a probability of 1/5 diceroll = random.randint(0,5) if diceroll == 2: self.random_vgen() for element in reversed(a): for ele in element: #print 'cheking element is 1 : ' + str(ele) if ele == 1: lis.append(count) count = count + 1 print lis self.list = lis self.a = a print self.list for i in self.list: self.pieces[i] = Monster(i,WHITE) #self.squares[i].setColor(HIGHLIGHT) print 'leaving trial' if len(self.list)==0: self.endscreen() break return def mouseTask(self, task): #This task deals with the highlighting and dragging based on the mouse #First, clear the current highlight if self.hiSq is not False: self.squares[self.hiSq].setColor(SquareColor(self.hiSq)) self.hiSq = False #Check to see if we can access the mouse. We need it to do anything else if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #If we are dragging something, set the position of the object #to be at the appropriate point over the plane of the board if self.dragging is not False: #Gets the point described by pickerRay.getOrigin(), which is relative to #camera, relative instead to render nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) #Same thing with the direction of the ray nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection()) try: self.pieces[self.dragging].obj.setPos(PointAtZ(.5, nearPoint, nearVec)) except: pass #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.squareRoot) if self.pq.getNumEntries() > 0: #if we have hit something, sort the hits so that the closest #is first, and highlight that node self.pq.sortEntries() i = int(self.pq.getEntry(0).getIntoNode().getTag('square')) #Set the highlight on the picked square self.squares[i].setColor(HIGHLIGHT) self.hiSq = i #print 'selected is ' + str(i) return Task.cont def grabPiece(self): #If a square is highlighted and it has a piece, set it to dragging mode if (self.hiSq is not False and self.pieces[self.hiSq]): self.dragging = self.hiSq self.hiSq = False def releasePiece(self): #Letting go of a piece. If we are not on a square, return it to its original #position. Otherwise, swap it with the piece in the new square if self.dragging is not False: #Make sure we really are dragging something #We have let go of the piece, but we are not on a square if self.hiSq is False: try: self.pieces[self.dragging].obj.setPos(SquarePos(self.dragging)) except: pass else: #Otherwise, swap the pieces self.swapPieces(self.dragging, self.hiSq) #self.draggin is the from print self.list print 'you picked this, so Imma deleting this ' + str(self.dragging) #deletion of i after picking it up. try: self.list.remove(self.dragging) except: pass temp2 = [] temp2 = SquarePosTuple(self.dragging) self.a[temp2[0]][temp2[1]] = 0 i = self.hiSq print self.list print 'highlighted sq is ' + str(i) templis = [] templis = SquarePosTuple(i) print templis self.list.append(i) print self.list print templis self.a[templis[0]][templis[1]] = 1 for line in self.a: print line self.trial() #We are no longer dragging anything self.dragging = False def setupLights(self): #This function sets up some default lighting ambientLight = AmbientLight( "ambientLight" ) ambientLight.setColor( Vec4(.8, .8, .8, 1) ) directionalLight = DirectionalLight( "directionalLight" ) directionalLight.setDirection( Vec3( 0, 45, -45 ) ) directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 1 ) ) render.setLight(render.attachNewNode( directionalLight ) ) render.setLight(render.attachNewNode( ambientLight ) )
class Mouse(object): def __init__(self, base, oid_texture): self.base = base self.picking_texture = oid_texture self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = self.base.cam.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask( CollisionNode.getDefaultCollideMask() | GeomNode.getDefaultCollideMask()) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) if settings.mouse_over: taskMgr.add(self.mouse_task, 'mouse-task') self.over = None def find_over_ray(self): over = None if self.base.mouseWatcherNode.hasMouse(): mpos = self.base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(self.base.camNode, mpos.getX(), mpos.getY()) self.picker.traverse(render) if self.pq.getNumEntries() > 0: self.pq.sortEntries() np = self.pq.getEntry(0).getIntoNodePath().findNetPythonTag( 'owner') owner = np.getPythonTag('owner') over = owner np = self.pq.getEntry(0).getIntoNodePath().findNetPythonTag( 'patch') if np is not None: self.patch = np.getPythonTag('patch') else: self.patch = None return over def find_over_color(self): over = None if self.base.mouseWatcherNode.hasMouse(): mpos = self.base.mouseWatcherNode.getMouse() self.base.graphicsEngine.extract_texture_data( self.picking_texture, self.base.win.gsg) texture_peeker = self.picking_texture.peek() if texture_peeker is not None: x = (mpos.get_x() + 1) / 2 y = (mpos.get_y() + 1) / 2 value = LColor() texture_peeker.lookup(value, x, y) oid = color_to_int(value) if oid != 0: over = objectsDB.get_oid(oid) if over is None: print("Unknown oid", oid, value) return over def find_over(self): if settings.color_picking: over_color = self.find_over_color() else: over_color = None over_ray = self.find_over_ray() over = over_color if over_ray is not None: if over is None or over.distance_to_obs > over_ray.distance_to_obs: over = over_ray if hasattr(over, "primary") and over.primary is not None: over = over.primary return over def get_over(self): if settings.mouse_over: over = self.over else: over = self.find_over() return over def mouse_task(self, task): if self.base.mouseWatcherNode.hasMouse(): self.over = self.find_over() return Task.cont
class Hero(DirectObject, MovableObject): def __init__(self, pos=(0, 0, 0), scale=1, parent=None): DirectObject.__init__(self) MovableObject.__init__(self) # 英雄属性 self._maxHP = defaultHeroHP self._HP = defaultHeroHP self._attackPower = defaultHeroAttackPower self._attackSpeed = defaultHeroAttackSpeed self.isMoving = False self._moveSpeed = defaultHeroMoveSpeed self.mousePos = (0, 0, 0) self.leapAttackTime = -1 #英雄被击闪烁 self.invincible = False #是否无敌 self.changeTime = -1 #下次变换形态时间 self.recoveryTime = -1 #恢复正常的时间 self.isHide = False ########## set model size hero self.bullet = SphereBullet( ) #(intoMask = CollideMask.bit(DefaultMonsterMaskVal)) if not self.bullet.model.isEmpty(): self.bullet.model.removeNode() self.attackMode = "Common" self.lastAttackTime = 0 # to enable shoutting at the beginning self.position = pos self.initAttackMethod = self.attack #'model_dierguanBOSS',{ # # 英雄模型和相应动画 self.model = Actor( HeroModelPath + "model_mainChara", { "Walk": HeroModelPath + "anim_mainChara_running_attack", #"Attack": HeroModelPath + "anim_mainChara_standing", "Hit": HeroModelPath + "anim_mainChara_standing", "Die": HeroModelPath + "anim_mainChara_standing" }) if parent == None: self.model.reparentTo(base.render) else: self.model.reparentTo(parent) self.model.setPos(self.position) self.lastPos = self.position self.scale = scale self.model.setScale(scale) self.model.hide() # 设置碰撞检测 self.colliderName = "hero" characterSphere = CollisionSphere(0, 0, 2, 1) characterColNode = CollisionNode(self.colliderName) characterColNode.addSolid(characterSphere) characterColNode.setFromCollideMask( CollideMask.bit(DefaultHeroMaskVal) ^ CollideMask.bit(defaultHeroInMonsterMaskVal)) # print characterColNode.getFromCollideMask() self.colliderNodePath = self.model.attachNewNode(characterColNode) #self.colliderNodePath.show() #将对象添加到nodepath中 用于在碰撞事件处理中获取对象 self.colliderNodePath.setPythonTag("Hero", self) base.cTrav.addCollider(self.colliderNodePath, base.cHandler) #用于处理英雄与墙壁的碰撞 characterSphere2 = CollisionSphere(0, 0, 2, 1) characterColNode2 = CollisionNode(self.colliderName) characterColNode2.addSolid(characterSphere2) self.colliderNodePath2 = self.model.attachNewNode(characterColNode2) self.modelGroundHandler = CollisionHandlerQueue() base.cTrav.addCollider(self.colliderNodePath2, self.modelGroundHandler) # #用于支持英雄与怪物的物理碰撞 characterSphere3 = CollisionSphere(0, 0, 2, 1) characterColNode3 = CollisionNode(self.colliderName) characterColNode3.addSolid(characterSphere3) self.colliderNodePath3 = self.model.attachNewNode(characterColNode3) base.cPusher.addCollider(self.colliderNodePath3, self.model) #用于支持鼠标控制英雄的朝向---------------- self.angle = 0 self.pickerName = 'mouseRay' self.pickerNode = CollisionNode(self.pickerName) self.pickerNP = camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(CollideMask.bit(5)) self.pickerNode.setIntoCollideMask(CollideMask.allOff()) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) #self.pickerNP.show() base.cTrav.addCollider(self.pickerNP, base.cHandler) self.pickedName = 'mousePlane' self.pickedNode = CollisionNode(self.pickedName) self.pickedNP = render.attachNewNode(self.pickedNode) self.pickedNode.setFromCollideMask(CollideMask.allOff()) self.pickedNode.setIntoCollideMask(CollideMask.bit(5)) self.pickedPlane = CollisionPlane( LPlane(LVector3f(0, 0, 1), LPoint3f(0, 0, 2))) self.pickedNode.addSolid(self.pickedPlane) #self.pickedNP.show() #------------------------------------ #加载英雄的各种音效 self.sounds["GetItem"] = loader.loadSfx(HeroSoundPath + "getItem.wav") self.sounds["Die"] = loader.loadSfx(HeroSoundPath + "robot_death.wav") self.sounds["Attack"] = loader.loadSfx(HeroSoundPath + "bullet_shooting.wav") #键位字典 self.keyMap = { "left": 0, "right": 0, "forward": 0, "back": 0, "fire": 0 } self._initAccept() def _initAccept(self): againEvent = '{}-again-{}' self.accept(againEvent.format(self.pickerName, self.pickedName), self._accept_ray_into_plane) def _accept_ray_into_plane(self, entry): pos = entry.getSurfacePoint(render) self.mousePos = LPoint3(pos[0], pos[1], pos[2]) x, y = pos.get_x() - self.model.getPos().get_x(), pos.get_y( ) - self.model.getPos().get_y() angle = math.atan2(y, x) angle = angle * 180 / math.pi + 90 self.angle = angle # maxHP @property def maxHP(self): return self._maxHP @maxHP.setter def maxHP(self, value): if value > defaultHeroMaxHPMax: value = defaultHeroMaxHPMax self._maxHP = value # HP @property def HP(self): return self._HP @HP.setter def HP(self, value): if value > self._maxHP: value = self._maxHP self._HP = value # attackPower @property def attackPower(self): return self._attackPower @attackPower.setter def attackPower(self, value): if value > defaultHeroAttackPowerMax: value = defaultHeroAttackPowerMax self._attackPower = value # attackSpeed @property def attackSpeed(self): return self._attackSpeed @attackSpeed.setter def attackSpeed(self, value): if value > defaultHeroAttackSpeedMax: value = defaultHeroAttackSpeedMax self._attackSpeed = value # moveSpeed @property def moveSpeed(self): return self._moveSpeed @moveSpeed.setter def moveSpeed(self, value): if value > defaultHeroMoveSpeedMax: value = defaultHeroMoveSpeedMax self._moveSpeed = value def setPos(self, pos): self.position = pos self.model.setPos(pos) def setScale(self, scale): self.scale = scale self.model.setScale(scale) def setEnemy(self, enemy): if isinstance(enemy, Monster): #inEvent = "{}-into-{}".format(enemy.colliderName,self.colliderName ) inEvent = "{}-into-{}".format(self.colliderName, enemy.colliderName) self.accept(inEvent, self.underAttack) if isinstance(enemy, Bullet): inEvent = "{}-into-{}".format(self.colliderName, enemy.colliderName) base.cHandler.addInPattern(inEvent) enemy.accept(inEvent, enemy.action) def move(self): dt = base.globalClock.getDt() x = self.model.getX() y = self.model.getY() dis = self._moveSpeed * dt #控制移动 if self.keyMap['forward']: y += dis if self.keyMap['back']: y -= dis if self.keyMap['left']: x -= dis if self.keyMap['right']: x += dis if x == self.model.getX() and y == self.model.getY(): if self.isMoving: self.model.stop() self.model.pose("walk", 0) self.isMoving = False elif not self.isMoving: self.model.loop('Walk') self.isMoving = True self.model.setX(x) self.model.setY(y) # 控制面朝向 self.updateDirection() #是否与房间(墙壁)碰撞 base.cTrav.traverse(base.render) entries = list(self.modelGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(base.render).getZ()) backup = False for entry in entries: if entry.getIntoNode().getName() == "room": backup = True break if backup: self.model.setPos(self.lastPos) else: self.lastPos = self.model.getPos() #base.camera.setPos(self.model.getX(), self.model.getY()+20, 20) #base.camera.lookAt(self.model) def updateDirection(self): self.model.setH(self.angle) def updateRay(self): ''' 在主循环中更新 人物朝向控制射线的方向 ''' if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) def attack(self, taskTime): ''' taskTime 无效参数,可以重新赋值 ''' angle = self.angle # 是否过了攻击冷却期 currTime = time.time() split = currTime - self.lastAttackTime if standarHitRate * 1.0 / self.attackSpeed > split: return # 更新上一次攻击的时间 self.lastAttackTime = currTime # 播放攻击动画 self.model.play("Attack") # 播放攻击音效 self.sounds["Attack"].play() # 子弹位置 pos = self.model.getPos() bullet = self.bullet.copy() #SphereBullet()#copy.deepcopy(self.bullet) bullet.model.reparentTo(render) bullet.setPos(pos) bullet.setZ(2) # bullet.getZ() + # 子弹生命周期(消亡的时间) bullet.setExpires(currTime + bullet.bulletLife) #bullet.bulletLife # 子弹飞行方向 bullet.setAngle(angle) # 子弹伤害值 ( 子弹本身伤害值 + 英雄攻击力 ) bullet.damage += self.attackPower bullet.model.show() # 注册子弹 base.messenger.send("bullet-create", [bullet]) def leapAttack(self, pos, size, speed): if self.leapAttackTime > time.time(): return self.leapAttackTime = time.time() + 2 bullet = LeapBullet(pos, DefaultMonsterMaskVal, size, speed) base.messenger.send("bullet-create", [bullet]) def update(self): self.updateInvincible() self.updateRay() self.move() self.updateDirection() if self.keyMap["fire"]: self.attack(time.time) self.keyMap["fire"] = False def updateInvincible(self): #处理无敌状态 if self.recoveryTime > time.time(): self.invincible = True if self.changeTime < time.time(): if self.isHide: self.isHide = False self.model.show() self.changeTime = time.time() + 0.2 else: self.isHide = True self.model.hide() self.changeTime = time.time() + 0.2 else: self.model.show() self.invincible = False def underAttack(self, val=defaultHeroHitByMonsterDamageVal): if self.invincible == True: return self.model.play("Hit") self.decreaseHP(val) self.recoveryTime = time.time() + 1.5 def decreaseHP(self, val): self.HP -= val if self.HP < 0: self.HP = 0 base.messenger.send("hero-HPChange", [self.HP]) if self.HP == 0: if Debug: return self.die() def increaseHP(self, val): self.HP += val if self.HP > self.maxHP: self.HP = self.maxHP base.messenger.send("hero-HPChange", [self.HP]) def increaseMaxHP(self, val): self.maxHP += val base.messenger.send("hero-maxHPChange", [self.maxHP]) def die(self): self.doMethodLater(self.model.getDuration("Die"), self.destroy, "hero-die") self.model.play("Die") self.sounds["Die"].play() # 防止继续移动 旋转 self.ignoreAll() base.taskMgr.remove("hero-Loop") def destroy(self, task): base.messenger.send("hero-die") @staticmethod def ToDict(hero): dict = {} dict["maxHP"] = hero.maxHP dict["HP"] = hero.HP dict["attackPower"] = hero.attackPower dict["attackSpeed"] = hero.attackSpeed dict["moveSpeed"] = hero._moveSpeed dict["bullet"] = hero.bullet.ToDict(hero.bullet) dict["pos"] = [hero.position[0], hero.position[1], hero.position[2]] dict["scale"] = hero.scale dict["attackMode"] = hero.attackMode return dict @staticmethod def ToHero(dict): hero = Hero() hero.maxHP = dict["maxHP"] hero.HP = dict["HP"] hero.attackPower = dict["attackPower"] hero.attackSpeed = dict["attackSpeed"] hero.setPos(LVecBase3f(dict["pos"][0], dict['pos'][1], dict['pos'][2])) hero.setScale(dict["scale"]) hero._moveSpeed = dict["moveSpeed"] hero.attackMode = dict["attackMode"] hero.bullet = BulletFactory.getBullet(dict["bullet"], hero) return hero def reInit(self, dict, renew=False): self.maxHP = dict["maxHP"] self.HP = dict["HP"] self.attackPower = dict["attackPower"] self.attackSpeed = dict["attackSpeed"] self._moveSpeed = dict["moveSpeed"] self.attackMode = dict["attackMode"] self.bullet = BulletFactory.getBullet(dict["bullet"], self) self.setPos(LVecBase3f(dict["pos"][0], dict['pos'][1], dict['pos'][2])) self.setScale(dict["scale"]) if renew: self.attack = self.initAttackMethod self._initAccept()
class InputManager(DirectObject): def __init__(self, base, lookatpos, pggen, togglerotcenter=False): self.base = base self.originallookatpos = lookatpos # for backup self.lookatpos_pdv3 = Vec3(lookatpos[0], lookatpos[1], lookatpos[2]) self.camdist = (self.base.cam.getPos() - self.lookatpos_pdv3).length() self.pggen = pggen self.initviewdist = (self.base.cam.getPos() - self.lookatpos_pdv3).length() self.wheelscale_distance = 150 self.lastm1pos = None self.lastm2pos = None # toggle on the following part to explicitly show the rotation center self.togglerotcenter = togglerotcenter if self.togglerotcenter: self.rotatecenternp = self.base.p3dh.gensphere( pos=self.originallookatpos, radius=5, rgba=np.array([1, 1, 0, 1])) self.rotatecenternp.reparentTo(self.base.render) # for resetting self.original_cam_pdmat4 = Mat4(self.base.cam.getMat()) self.keymap = { "mouse1": False, "mouse2": False, "mouse3": False, "wheel_up": False, "wheel_down": False, "space": False, "w": False, "s": False, "a": False, "d": False, "g": False, "r": False } self.accept("mouse1", self.__setkeys, ["mouse1", True]) self.accept("mouse1-up", self.__setkeys, ["mouse1", False]) self.accept("mouse2", self.__setkeys, ["mouse2", True]) self.accept("mouse2-up", self.__setkeys, ["mouse2", False]) self.accept("mouse3", self.__setkeys, ["mouse3", True]) self.accept("mouse3-up", self.__setkeys, ["mouse3", False]) self.accept("wheel_up", self.__setkeys, ["wheel_up", True]) self.accept("wheel_down", self.__setkeys, ["wheel_down", True]) self.accept("space", self.__setkeys, ["space", True]) self.accept("space-up", self.__setkeys, ["space", False]) self.accept("w", self.__setkeys, ["w", True]) self.accept("w-up", self.__setkeys, ["w", False]) self.accept("s", self.__setkeys, ["s", True]) self.accept("s-up", self.__setkeys, ["s", False]) self.accept("a", self.__setkeys, ["a", True]) self.accept("a-up", self.__setkeys, ["a", False]) self.accept("d", self.__setkeys, ["d", True]) self.accept("d-up", self.__setkeys, ["d", False]) self.accept("g", self.__setkeys, ["g", True]) self.accept("g-up", self.__setkeys, ["g", False]) self.accept("r", self.__setkeys, ["r", True]) self.accept("r-up", self.__setkeys, ["r", False]) self.setup_interactiongeometries() def __setkeys(self, key, value): self.keymap[key] = value return def setup_interactiongeometries(self): """ set up collision rays, spheres, and planes for mouse manipulation :return: None author: weiwei date: 20161110 """ # create a trackball ray and set its bitmask to 8 # the trackball ray must be a subnode of cam since we will # transform the clicked point (in the view of the cam) to the world coordinate system # using the ray self.tracker_cn = CollisionNode("tracker") self.tracker_ray = CollisionRay() self.tracker_cn.addSolid(self.tracker_ray) self.tracker_cn.setFromCollideMask(BitMask32.bit(8)) self.tracker_cn.setIntoCollideMask(BitMask32.allOff()) self.tracker_np = self.base.cam.attachNewNode(self.tracker_cn) # create an inverted collision sphere and puts it into a collision node # its bitmask is set to 8, and it will be the only collidable object at bit 8 self.trackball_cn = CollisionNode("trackball") self.trackball_cn.addSolid( CollisionSphere(self.lookatpos_pdv3[0], self.lookatpos_pdv3[1], self.lookatpos_pdv3[2], self.camdist)) self.trackball_cn.setFromCollideMask(BitMask32.allOff()) self.trackball_cn.setIntoCollideMask(BitMask32.bit(8)) self.trackball_np = self.base.render.attachNewNode(self.trackball_cn) # self.trackball_np.show() # This creates a collision plane for mouse track self.trackplane_cn = CollisionNode("trackplane") # self.aimPlaneCN.addSolid(CollisionPlane(Plane(Vec3(0, 0, 1), self.lookatpos_pdv3))) # self.trackplane_cn.addSolid(CollisionBox(Point3(self.lookatpos_pdv3[0], self.lookatpos_pdv3[1], 0.1), 1e6, 1e6, 1e-6)) self.trackplane_cn.addSolid( CollisionPlane( Plane( Point3(self.base.cam.getMat().getRow3(2) - self.base.cam.getMat().getRow3(1)), Point3(self.lookatpos_pdv3[0], self.lookatpos_pdv3[1], 0.1)))) self.trackplane_cn.setFromCollideMask(BitMask32.allOff()) self.trackplane_cn.setIntoCollideMask(BitMask32.bit(8)) self.trackplane_np = self.base.render.attachNewNode(self.trackplane_cn) # self.trackplane_np.show() # creates a traverser to do collision testing self.ctrav = CollisionTraverser() # creates a queue type handler to receive the collision event info self.chandler = CollisionHandlerQueue() # register the ray as a collider with the traverser, # and register the handler queue as the handler to be used for the collisions. self.ctrav.addCollider(self.tracker_np, self.chandler) # create a pickerray self.picker_cn = CollisionNode('picker') self.picker_ray = CollisionRay() self.picker_cn.addSolid(self.picker_ray) self.picker_cn.setFromCollideMask(BitMask32.bit(7)) self.picker_cn.setIntoCollideMask(BitMask32.allOff()) self.picker_np = self.base.cam.attachNewNode(self.picker_cn) self.ctrav.addCollider(self.picker_np, self.chandler) def shift_trackballsphere(self, center=np.array([0, 0, 0])): self.camdist = (self.base.cam.getPos() - self.lookatpos_pdv3).length() self.trackball_cn.setSolid( 0, CollisionSphere(center[0], center[1], center[2], self.camdist)) def shift_trackplane(self): self.trackplane_cn.setSolid( 0, CollisionPlane( Plane( Point3(self.base.cam.getMat().getRow3(2) - self.base.cam.getMat().getRow3(1)), Point3(self.lookatpos_pdv3[0], self.lookatpos_pdv3[1], 0.1)))) def get_world_mouse1(self): """ 给et the position of mouse1 (clicked) using collision detection between a sphere and a ray :return: Vec3 or None author: weiwei date: 20161110 """ if self.base.mouseWatcherNode.hasMouse(): if self.keymap['mouse1']: # get the mouse position in the window mpos = self.base.mouseWatcherNode.getMouse() # sets the ray's origin at the camera and directs it to shoot through the mouse cursor self.tracker_ray.setFromLens(self.base.cam.node(), mpos.getX(), mpos.getY()) # performs the collision checking pass self.ctrav.traverse(self.trackball_np) if (self.chandler.getNumEntries() > 0): # Sort the handler entries from nearest to farthest self.chandler.sortEntries() entry = self.chandler.getEntry(0) colPoint = entry.getSurfacePoint(self.base.render) return colPoint return None def check_mouse1drag(self): """ this function uses a collision sphere to track the rotational mouse motion :return: author: weiwei date: 20200315 """ curm1pos = self.get_world_mouse1() if curm1pos is None: if self.lastm1pos is not None: self.lastm1pos = None return if self.lastm1pos is None: # first time click self.lastm1pos = curm1pos return curm1vec = Vec3(curm1pos - self.lookatpos_pdv3) lastm1vec = Vec3(self.lastm1pos - self.lookatpos_pdv3) curm1vec.normalize() lastm1vec.normalize() rotatevec = curm1vec.cross(lastm1vec) if rotatevec.length() > 1e-10: # avoid zero length rotateangle = curm1vec.signedAngleDeg(lastm1vec, rotatevec) rotateangle = rotateangle * self.camdist * 5 if rotateangle > .02 or rotateangle < -.02: rotmat = Mat4(self.base.cam.getMat()) posvec = Vec3(self.base.cam.getPos()) rotmat.setRow(3, Vec3(0, 0, 0)) self.base.cam.setMat(rotmat * Mat4.rotateMat(rotateangle, rotatevec)) self.base.cam.setPos(Mat3.rotateMat(rotateangle, rotatevec). \ xform(posvec - self.lookatpos_pdv3) + self.lookatpos_pdv3) self.lastm1pos = self.get_world_mouse1() self.shift_trackplane() def get_world_mouse2(self): if self.base.mouseWatcherNode.hasMouse(): if self.keymap['mouse2']: mpos = self.base.mouseWatcherNode.getMouse() self.tracker_ray.setFromLens(self.base.cam.node(), mpos.getX(), mpos.getY()) self.ctrav.traverse(self.trackplane_np) self.chandler.sortEntries() if (self.chandler.getNumEntries() > 0): entry = self.chandler.getEntry(0) colPoint = entry.getSurfacePoint(self.base.render) return colPoint return None def check_mouse2drag(self): """ :return: author: weiwei date: 20200313 """ curm2pos = self.get_world_mouse2() if curm2pos is None: if self.lastm2pos is not None: self.lastm2pos = None return if self.lastm2pos is None: # first time click self.lastm2pos = curm2pos return relm2vec = curm2pos - self.lastm2pos if relm2vec.length() > 1: self.base.cam.setPos(self.base.cam.getPos() - relm2vec) self.lookatpos_pdv3 = Vec3(self.lookatpos_pdv3 - relm2vec) newlookatpos = self.base.p3dh.pdv3_to_npv3(self.lookatpos_pdv3) if self.togglerotcenter: self.rotatecenternp.detachNode() self.rotatecenternp = self.base.p3dh.gensphere( pos=newlookatpos, radius=5, rgba=np.array([1, 1, 0, 1])) self.rotatecenternp.reparentTo(self.base.render) self.shift_trackballsphere(self.lookatpos_pdv3) self.last2mpos = curm2pos def get_world_mouse3(self): """ picker ray :return: author: weiwei date: 20200316 """ if self.base.mouseWatcherNode.hasMouse(): if self.keymap['mouse3']: mpos = self.base.mouseWatcherNode.getMouse() self.picker_ray.setFromLens(self.base.cam.node(), mpos.getX(), mpos.getY()) self.ctrav.traverse(self.base.render) if (self.chandler.getNumEntries() > 0): self.chandler.sortEntries() entry = self.chandler.getEntry(0) colPoint = entry.getSurfacePoint(self.base.render) return colPoint return None def check_mouse3click(self): """ :return: author: weiwei date: 20200316 """ curm3pos = self.get_world_mouse3() if curm3pos is None: return else: print(curm3pos) def check_mousewheel(self): """ zoom up or down the 3d view considering mouse action author: weiwei date: 2016, 20200313 :return: """ if self.keymap["wheel_up"] is True: self.keymap["wheel_up"] = False backward = self.base.cam.getPos() - self.lookatpos_pdv3 backward.normalize() newpos = self.base.cam.getPos( ) + backward * self.wheelscale_distance if newpos.length() < self.initviewdist * 20: self.base.cam.setPos(newpos[0], newpos[1], newpos[2]) self.shift_trackballsphere( self.trackball_cn.getSolid(0).getCenter()) if self.keymap["wheel_down"] is True: self.keymap["wheel_down"] = False forward = self.lookatpos_pdv3 - self.base.cam.getPos() if forward.length() < self.wheelscale_distance: return forward.normalize() newpos = self.base.cam.getPos( ) + forward * self.wheelscale_distance if newpos.length() > self.initviewdist * .05: self.base.cam.setPos(newpos[0], newpos[1], newpos[2]) self.shift_trackballsphere( self.trackball_cn.getSolid(0).getCenter()) def check_mouse1drag_trackball(self): """ This function uses the stereographic projection introduced in https://en.wikipedia.org/wiki/Stereographic_projection to track the rotational mouse motion Equations: [e, f] -> [x, y, z] = [2e/(1+e^2+f^2), 2f/(1+e^2+f^2), (-1+e^2+f^2)/(2+2e^2+2f^2)] :return: author: weiwei date: 20200315 """ def cvtvirtualtrackball(screenx, screeny, psec_squared=1 / 4): """ convert a screen point to virtual trackball coordinate psec indicates the size of the spherical section to be mapped to default radius = 1 :param screenx: :param screeny: :param psec_squared: :return: author: weiwei date: 20200315 """ screenpoint_squaredsum = screenx**2 + screeny**2 trackballx = 2 * psec_squared * screenx / (psec_squared + screenpoint_squaredsum) trackballz = 2 * psec_squared * screeny / (psec_squared + screenpoint_squaredsum) trackbally = -math.sqrt(1 - trackballx**2 - trackballz**2) returnvec = Vec3(trackballx, trackbally, trackballz) returnvec.normalize() return returnvec currentmouse = self.base.mouseWatcherNode.getMouse() curm1pos = [currentmouse.getX(), currentmouse.getY()] if curm1pos is None: if self.lastm1pos is not None: self.lastm1pos = None return if self.lastm1pos is None: # first time click self.lastm1pos = curm1pos return curm1vec_pdv3 = cvtvirtualtrackball(curm1pos[0], curm1pos[1]) lastm1vec_pdv3 = cvtvirtualtrackball(self.lastm1pos[0], self.lastm1pos[1]) rotatevec_pdv3 = curm1vec_pdv3.cross(lastm1vec_pdv3) rotateangle = curm1vec_pdv3.signedAngleDeg(lastm1vec_pdv3, rotatevec_pdv3) if rotateangle > .02 or rotateangle < -.02: rotateangle = rotateangle * 5 camrotmat_pd = self.base.cam.getMat().getUpper3() calibrated_camrotmat_pd = Mat3.rotateMat( rotateangle, camrotmat_pd.xformVec(rotatevec_pdv3)) posvec_pd = self.base.cam.getPos() self.base.cam.setMat(Mat4.identMat()) self.base.cam.setMat(camrotmat_pd * calibrated_camrotmat_pd) self.base.cam.setPos( calibrated_camrotmat_pd.xform(posvec_pd - self.lookatpos_pdv3) + self.lookatpos_pdv3) self.lastm1pos = curm1pos[:] def check_resetcamera(self): """ reset the rendering window to its initial viewpoint :return: author: weiwei date: 20200316 """ if self.keymap["r"] is True: self.keymap["r"] = False self.base.cam.setMat(self.original_cam_pdmat4) self.lookatpos_pdv3 = self.base.p3dh.npv3_to_pdv3( self.originallookatpos) # toggle on the following part to explicitly show the rotation center if self.togglerotcenter: self.rotatecenternp.detachNode() self.rotatecenternp = self.base.p3dh.gensphere( pos=self.originallookatpos, radius=5, rgba=np.array([1, 1, 0, 1])) self.rotatecenternp.reparentTo(self.base.render)
class MousePicker(object): def __init__(self, pickTag="MyPickingTag", nodeName="pickRay", showCollisions=False): self.pickTag = pickTag self.nodeName = nodeName self.showCollisions = showCollisions def create(self): self.mPickerTraverser = CollisionTraverser() self.mCollisionQue = CollisionHandlerQueue() self.mPickRay = CollisionRay() self.mPickRay.setOrigin(base.camera.getPos(base.render)) self.mPickRay.setDirection(base.render.getRelativeVector(base.camera, Vec3(0, 1, 0))) # create our collison Node to hold the ray self.mPickNode = CollisionNode(self.nodeName) self.mPickNode.addSolid(self.mPickRay) # Attach that node to the camera since the ray will need to be positioned # relative to it, returns a new nodepath # well use the default geometry mask # this is inefficent but its for mouse picking only self.mPickNP = base.camera.attachNewNode(self.mPickNode) # we'll use what panda calls the "from" node. This is reall a silly convention # but from nodes are nodes that are active, while into nodes are usually passive environments # this isnt a hard rule, but following it usually reduces processing # Everything to be picked will use bit 1. This way if we were doing other # collision we could seperate it, we use bitmasks to determine what we check other objects against # if they dont have a bitmask for bit 1 well skip them! self.mPickNode.setFromCollideMask(BitMask32(1)) # Register the ray as something that can cause collisions self.mPickerTraverser.addCollider(self.mPickNP, self.mCollisionQue) # Setup 2D picker self.mPickerTraverser2D = CollisionTraverser() self.mCollisionQue2D = CollisionHandlerQueue() self.mPickNode2D = CollisionNode("2D PickNode") self.mPickNode2D.setFromCollideMask(BitMask32(1)) self.mPickNode2D.setIntoCollideMask(BitMask32.allOff()) self.mPick2DNP = base.camera2d.attachNewNode(self.mPickNode2D) self.mPickRay2D = CollisionRay() self.mPickNode2D.addSolid(self.mPickRay2D) self.mPickerTraverser2D.addCollider(self.mPick2DNP, self.mCollisionQue2D) if self.showCollisions: self.mPickerTraverser.showCollisions(base.render) self.mPickerTraverser2D.showCollisions(base.aspect2d) def mousePick(self, traverse=None, tag=None): # do we have a mouse if base.mouseWatcherNode.hasMouse() == False: return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() # Set the position of the ray based on the mouse position self.mPickRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.mPickerTraverser.traverse(traverse) if self.mCollisionQue.getNumEntries() > 0: self.mCollisionQue.sortEntries() for entry in self.mCollisionQue.getEntries(): pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.render) return pickedObj, pos return None, None def mousePick2D(self, traverse=None, tag=None, all=False): # do we have a mouse if base.mouseWatcherNode.hasMouse() == False: return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() self.mPickRay2D.setFromLens(base.cam2d.node(), mpos.getX(), mpos.getY()) self.mPickerTraverser2D.traverse(base.aspect2d) if self.mCollisionQue2D.getNumEntries() > 0: self.mCollisionQue2D.sortEntries() if all: return ( [ (entry.getIntoNodePath().findNetTag(tag), entry.getSurfacePoint(base.aspect2d)) for entry in self.mCollisionQue2D.getEntries() ], None, ) else: entry = self.mCollisionQue2D.getEntry(0) pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.aspect2d) return pickedObj, pos return None, None
class World(DirectObject): def __init__(self, mode, ip=None): if mode==CLIENT and not ip: #Don't let this happen. print "WTF programmer" sys.exit() #current dialog box self.d = None #top-left of screen; contains instructions on how to exit the game. self.quitInstructions = OnscreenText(text='Press ESC to exit.', pos=(-1, 0.95), scale=0.05, fg=(1,1,1,1), bg=(0,0,0,0), mayChange=False) #bottom of screen self.turnIndicator = OnscreenText(text='', pos=(0,-0.8), scale=0.1, fg=(1,1,1,1), bg=(0,0,0,0), mayChange=True) #Saving some values, some default values self.mode = mode self.player = {SERVER: PIECEWHITE, CLIENT: PIECEBLACK}[self.mode] self.ip = ip #Panda3D, by default, allows for camera control with the mouse. base.disableMouse() self.setupMouse() self.setupBoard() self.setupCamera() self.setupPieces() self.setupNetwork() self.setupLights() #some internal state for making clicky moves self.hiSq = None self.dragOrigin = None #keyboard, mouse self.mouseTask = taskMgr.add(self.tskMouse, 'mouseTask') self.accept('mouse1', self.handleClick) self.accept('f2', lambda: base.setFrameRateMeter(True)) self.accept('f3', lambda: base.setFrameRateMeter(False)) self.accept('escape', sys.exit) #first turn self.turn = PIECEWHITE #### INITIALIZATION #### def setupBoard(self): #We will attach all of the squares to their own root. This way we can do the #collision pass just on the sqaures and save the time of checking the rest #of the scene self.squareRoot = render.attachNewNode("squareRoot") #For each square self.squares = dict(((i,j), None) for i in range(8) for j in range(8)) for place in self.squares: #Load, parent, color, and position the model (a single square polygon) self.squares[place] = loader.loadModel("models/square") self.squares[place].reparentTo(self.squareRoot) self.squares[place].setPos(SquarePos(place)) self.squares[place].setColor(SquareColor(place)) #Set the model itself to be collideable with the ray. If this model was #any more complex than a single polygon, you should set up a collision #sphere around it instead. But for single polygons this works fine. self.squares[place].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) #Set a tag on the square's node so we can look up what square this is #later during the collision pass self.squares[place].find("**/polygon").node().setTag('square', ' '.join(map(str,place))) self.squares[place].setTransparency(TransparencyAttrib.MAlpha) def setupPieces(self): #Default dictionaries work decently well as an easy two-dimensional array. self.pieces = defaultdict(lambda: None) #The order of pieces on a chessboard from white's perspective pieceOrder = [Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook] for i in xrange(8): #load white pawns self.pieces[i, 1] = Pawn((i,1), PIECEWHITE) #load black pawns self.pieces[i, 6] = Pawn((i, 6), PIECEBLACK) #load white specials self.pieces[i, 0] = pieceOrder[i]((i,0), PIECEWHITE) #load black specials self.pieces[i, 7] = pieceOrder[i]((i,7), PIECEBLACK) for p in self.pieces.values(): p.obj.setTransparency(TransparencyAttrib.MAlpha) # TODO: Notice when the other side disconnects def setupNetwork(self): if self.mode == CLIENT: self.setupClient(self.ip) else: self.setupServer() # A lot of the below two methods is boilerplate straight from Panda3D documentation. def setupServer(self): self.cManager = QueuedConnectionManager() self.cListener = QueuedConnectionListener(self.cManager, 0) self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager,0) self.oppConnection = None port = 15905 # Chosen by fair dice roll. # Guaranteed to be random. backlog = 1000 tcpSocket = self.cManager.openTCPServerRendezvous(port, backlog) self.cListener.addConnection(tcpSocket) def tskListenerPoll(task): if self.cListener.newConnectionAvailable() and not self.oppConnection: rendezvous = PointerToConnection() addr = NetAddress() newCon = PointerToConnection() if self.cListener.getNewConnection(rendezvous, addr, newCon): newConnection = newCon.p() print "Received connection from %s" % newConnection.getAddress() self.oppConnection = newConnection self.cReader.addConnection(newConnection) #server starts the game self.turnIndicator['text'] = 'Your turn!' self.showVisibleSquares() #remove the dialog node from below if self.d: self.d.removeNode() return Task.done if not self.d: self.d = DirectDialog(text="Waiting for client to connect...", buttonTextList=[], buttonValueList=[]) return Task.cont taskMgr.add(tskListenerPoll, "Poll the connection listener") taskMgr.add(self.tskReaderPoll, "Poll the connection reader") def setupClient(self, ip): self.cManager = QueuedConnectionManager() self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager,0) self.oppConnection = None port = 15905 timeout = 3000 myConnection = self.cManager.openTCPClientConnection(ip, port, timeout) if myConnection: self.cReader.addConnection(myConnection) self.oppConnection = myConnection taskMgr.add(self.tskReaderPoll, "Poll the connection reader") self.showVisibleSquares() else: self.d = OkDialog(text="Could not connect to server at '%s'" % ip, command=sys.exit) # Makes sure player gets a decent view of the game board, and *not* of the hidden pieces below the board. Shhhh... def setupCamera(self): if self.player == PIECEWHITE: camera.setPos(0, -13.75, 8) camera.lookAt(self.squareRoot) camera.setH(0) else: camera.setPos(0, 13.75, 8) camera.lookAt(self.squareRoot) camera.setH(180) # Adds some ambient lights and a directional light def setupLights(self): #This is one area I know hardly anything about. I really don't know how to get this to behave nicely. #The black pieces are hardly distinguishable. ambientLight = AmbientLight( "ambientLight" ) ambientLight.setColor( Vec4(.8, .8, .8, 1) ) directionalLight = DirectionalLight( "directionalLight" ) directionalLight.setDirection( Vec3( 0, 45, -45 ) ) directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 1 ) ) render.setLight(render.attachNewNode( directionalLight ) ) render.setLight(render.attachNewNode( ambientLight ) ) # Sets up collision detection for the mouse cursor. def setupMouse(self): #Since we are using collision detection to do picking, we set it up like #any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() #Make a traverser self.pq = CollisionHandlerQueue() #Make a handler #Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') #Attach that node to the camera since the ray will need to be positioned #relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() #Make our ray self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node #Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #### TASKS #### # Checks for incoming data on the connection def tskReaderPoll(self, task): if self.cReader.dataAvailable(): datagram = NetDatagram() if self.cReader.getData(datagram): self.receiveData(datagram) return Task.cont # Runs every frame, checks whether the mouse is highlighting something or another def tskMouse(self, task): #This task deals with the highlighting and dragging based on the mouse #First, clear the current highlight if self.hiSq: self.squares[self.hiSq].setColor(SquareColor(self.hiSq)) self.hiSq = None #Check to see if we can access the mouse. We need it to do anything else if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #If we are dragging something, set the position of the object #to be at the appropriate point over the plane of the board if self.dragOrigin: #camera, relative instead to render #Gets the point described by pickerRay.getOrigin(), which is relative to nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) #Same thing with the direction of the ray nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection()) self.pieces[self.dragOrigin].obj.setPos( PointAtZ(.5, nearPoint, nearVec)) #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.squareRoot) if self.pq.getNumEntries() > 0: #if we have hit something, sort the hits so that the closest #is first, and highlight that node self.pq.sortEntries() p = tuple(map(int, (self.pq.getEntry(0).getIntoNode().getTag('square')).split())) if self.pieces[p] and self.pieces[p].color == self.turn == self.player and not self.dragOrigin or self.dragOrigin and self.pieces[self.dragOrigin].isValidMove(p, self.pieces): #Set the highlight on the picked square self.hiSq = p self.squares[self.hiSq].setColor(HIGHLIGHT) return Task.cont def handleClick(self): # Disabled when a dialog box is on-screen. Pay attention to what I'm telling you, user! if not self.d: if self.dragOrigin: self.releasePiece() else: self.grabPiece() # Comes from handleClick def grabPiece(self): #If a square is highlighted and it has a piece, set it to dragging mode if self.hiSq and self.pieces[self.hiSq] and self.pieces[self.hiSq].color == self.turn: self.dragOrigin = self.hiSq self.hiSq = None def releasePiece(self): #Letting go of a piece. If we are not on a square, return it to its original #position. if self.dragOrigin: #Make sure we really are dragging something if self.hiSq and self.hiSq != self.dragOrigin and self.pieces[self.dragOrigin].isValidMove(self.hiSq, self.pieces): # Verify that this doesn't put the king in check # Make backup of the pieces dictionary oldPieces = self.pieces.copy() self.pieces[self.hiSq] = self.pieces[self.dragOrigin] self.pieces[self.dragOrigin] = None if self.inCheck(self.turn): self.pieces = oldPieces self.pieces[self.dragOrigin].obj.setPos(SquarePos(self.dragOrigin)) print "Invalid move -- King is in check" def closeDialog(): self.d.removeNode() self.d = OkDialog(text="That move would put your King in check!", command=closeDialog) else: self.pieces = oldPieces self.makeMove(self.dragOrigin, self.hiSq, dt=0, callback=self.showVisibleSquares).start() self.sendMove(self.dragOrigin, self.hiSq) self.squares[self.dragOrigin].setColor(SquareColor(self.dragOrigin)) #no longer our turn self.turnIndicator['text'] = '' else: self.pieces[self.dragOrigin].obj.setPos(SquarePos(self.dragOrigin)) print "Invalid move" #We are no longer dragging anything self.dragOrigin = False #### CHESS UPDATES #### # Moves a piece from one space to another. # This should be called to update internal state, whether the piece is already in the correct location or not. # Also handles captures. def makeMove(self, fr, to, dt=1, callback=None): print "Making move %s -> %s" % (str(fr), str(to)) frP = self.pieces[fr] toP = self.pieces[to] if not frP: return False if toP and frP.color == toP.color: return False if not frP.isValidMove(to, self.pieces): return False # Callback function for the movement. # Updates pieces' internal state, as well as the true state of the board (self.pieces) def updateState(): self.destroy(toP) frP.square = to frP.haveMoved = True self.pieces[fr] = None self.pieces[to] = frP self.turn = flip[self.turn] if self.inCheck(self.player): def dismiss(val): self.d.removeNode() self.d = OkDialog(text="You are in check!", command=dismiss) s = Sequence( frP.obj.posInterval(dt, self.squares[to].getPos()), Func(updateState) ) if callback: s.append(Func(callback)) return s # Removes the piece. This method is passed a Piece object, not a location! # Possible improvements: Particle effects! :D def destroy(self, piece): if piece: piece.obj.removeNode() # Determines whether the player specified by "color" is in check at the current time # Future improvements: Calculate the same thing for possible future moves (i.e. if I move here am I therefore in check?) def inCheck(self, color): #find the king kingPlace = [p for p in self.pieces if self.pieces[p] and self.pieces[p].color == color and self.pieces[p].model == "models/king"][0] for p in self.pieces: if self.pieces[p] and self.pieces[p].color != color and self.pieces[p].isValidMove(kingPlace, self.pieces): return True return False # Currently unused, but could be useful in an (extremely primitive) AI in the future. # I ran out of time to put it in this version. def makeRandomMove(self): move = None while not move: chosenPiece = random.choice([(x,y) for (x,y) in self.pieces if 0 <= x < 8 and 0 <= y < 8 and self.pieces[x,y] and self.pieces[x,y].color == self.turn]) if not self.pieces[chosenPiece].validMoves(self.pieces): continue destSquare = random.choice([s for s in self.pieces[chosenPiece].validMoves(self.pieces)]) move = (chosenPiece, destSquare) self.makeMove(*move).start() #### VISIBILITY UPDATES #### def isVisible(self, sq): return self.squares[sq].getColorScale()[3] == 1.0 # The next two methods deal with hiding and showing the squares of the board. def hideSquare(self, sq, dt="default", callback=None): if self.squares[sq] and self.isVisible(sq): if dt == "default": dt = 1.0 par = Parallel( LerpFunctionInterval(self.squares[sq].setAlphaScale, toData=0.0, fromData=1.0, duration=dt), ) if self.pieces[sq]: par.append(LerpFunctionInterval(self.pieces[sq].obj.setAlphaScale, toData=0.0, fromData=1.0, duration=dt)) s = Sequence(par) if callback: s.append(Func(callback)) return s else: s = Sequence() if callback: s.append(Func(callback)) return s def showSquare(self, sq, dt="default", callback=None): if self.squares[sq] and not self.isVisible(sq): if dt == "default": dt = 1.0 par = Parallel( LerpFunctionInterval(self.squares[sq].setAlphaScale, toData=1.0, fromData=0.0, duration=dt), ) if self.pieces[sq]: par.append(LerpFunctionInterval(self.pieces[sq].obj.setAlphaScale, toData=1.0, fromData=0.0, duration=dt)) s = Sequence(par) if callback: s.append(Func(callback)) return s else: s = Sequence() if callback: s.append(Func(callback)) return s # Shows the path that a piece takes on its way IF any part of it is visible to the current player def showPathIfVisible(self, fr, to): if self.pieces[fr]: path = set() showSquareSequences = Parallel() if self.pieces[fr]: path.update(self.pieces[fr].path(to)) if any(self.isVisible(sq) for sq in path): for sq in path: showSquareSequences.append(self.showSquare(sq)) return showSquareSequences else: return Parallel() # Shows the path that a piece takes on its path from its origin to its destination def showPath(self, fr, to): path = set() showSquareSequences = Parallel() if self.pieces[fr]: path.update(self.pieces[fr].path(to)) for sq in path: showSquareSequences.append(self.showSquare(sq)) return showSquareSequences # Updates the board to show only the squares that are visible at the current time. def showVisibleSquares(self, dt="default"): visibles = defaultdict(lambda: False) for p in [(x,y) for (x,y) in self.pieces if 0 <= x < 8 and 0 <= y < 8]: if self.pieces[p]: if self.pieces[p].color == self.player: for s in self.pieces[p].visibleSquares(self.pieces): visibles[s] = True par = Parallel() for s in self.squares: if visibles[s]: par.append(self.showSquare(s, dt)) else: par.append(self.hideSquare(s, dt)) par.start() return par #### NETWORK I/O #### def sendMove(self, fr, to): dg = PyDatagram() dg.addUint8(fr[0]) dg.addUint8(fr[1]) dg.addUint8(to[0]) dg.addUint8(to[1]) print "Sent move (%d, %d) -> (%d, %d)" % (fr[0], fr[1], to[0], to[1]) self.cWriter.send(dg, self.oppConnection) def receiveData(self, dg): dg = PyDatagramIterator(dg) fr = (dg.getUint8(), dg.getUint8()) to = (dg.getUint8(), dg.getUint8()) print "Received move %s -> %s" % (fr, to) def indicate(): self.turnIndicator['text'] = 'Your turn!' self.sfx = loader.loadSfx('audio/ding.wav') self.sfx.play() seq = Sequence() seq.append(self.showPathIfVisible(fr, to)) seq.append(self.makeMove(fr, to)) seq.append(Func(indicate)) seq.append(Func(self.showVisibleSquares)) seq.start()
class FlyThroughAir(ShowBase): # method completely taken from RoamingRalph demo: def addInstructions(self, pos, msg): return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, shadow=(0, 0, 0, 1), parent=base.a2dTopLeft, pos=(0.08, -pos - 0.04), align=TextNode.ALeft) def setUpFlyingInstructions(self): self.inst[0] = self.addInstructions(.06, "Arrow Keys to move around") self.inst[1] = self.addInstructions(.12, "w and s to control pitch") self.inst[2] = self.addInstructions(.18, "a and d to control yaw") self.inst[3] = self.addInstructions(.24, "h to switch to driving mode") self.inst[4] = self.addInstructions(.3, "mouse click to add object") def destroyInstructions(self): # got way to destroy text from: # https://www.panda3d.org/manual/index.php/OnscreenText for element in self.inst: element.destroy() def setUpTreeButton(self): pass def setUpDrivingInstructions(self): self.inst[0] = self.addInstructions(.06, "Right arrow and left arrow to turn") self.inst[1] = self.addInstructions(.12, "Forward and Backward arrow to go forward and backward") self.inst[2] = self.addInstructions(.18, "h to switch to add object mode") def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) # Set the background color to black self.win.setClearColor((0, 1, 1, 1)) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0, "backward": 0, "cam-up": 0, "cam-down": 0, "add-car": 0, "switch-mode":0, "mouse-click":0} # this is the egg that came with the module # environ = loader.loadModel("models/world") # this is the one I created using mountainMaker.py self.environ = loader.loadModel("TestMountain1") self.environ.reparentTo(render) self.car = loader.loadModel("TestCar") #self.car = loader.loadModel("testModel/ball") self.car.reparentTo(render) #instructions self.inst = [""]*5 self.setUpFlyingInstructions() # for adjusting so that the position is the center of the car self.adjustedXForCenter = 10/2 self.adjustedYForCenter = 20/2 # important for setting the size relative to everything else # found it here : https://www.panda3d.org/manual/index.php/Common_State_Changes # set the mode that the player is currently in self.mode = 0 self.modeFly = 0 self.modeRace = 1 # to ensure that when pressing h it only switches once each press self.hasSwitched = False self.carPositionX = 10 self.carPositionY = 10 self.carPositionZ = 100 # note for rotating camera: from this website: # https://www.panda3d.org/manual/index.php/Common_State_Changes # setHpr(Yaw, Pitch, Roll) # setting up initial conditions for which way camera is rotated self.carYaw = 0 self.carPitch = 0 self.setUpCarCollider() self.setUpMouseCollider() # make the rocks and other stuff that will show up self.objects = [] # Accept the control keys for movement and rotation #setting up keys for movement self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) # adding car self.accept("mouse1", self.setKey, ["mouse-click", True]) self.accept("mouse1-up", self.setKey, ["mouse-click", False]) # setting up orientation of the camera self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-down", True]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-down", False]) self.accept("d", self.setKey, ["cam-right", True]) self.accept("d-up", self.setKey, ["cam-right", False]) self.accept("w", self.setKey, ["cam-up", True]) self.accept("w-up", self.setKey, ["cam-up", False]) # to switch between tasks self.accept("h", self.setKey, ["switch-mode", True]) self.accept("h-up", self.setKey, ["switch-mode", False]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False self.cameraPositionX = 0 self.cameraPositionY = 0 self.cameraPositionZ = 0 # note for rotating camera: from this website: # https://www.panda3d.org/manual/index.php/Common_State_Changes # setHpr(Yaw, Pitch, Roll) # setting up initial conditions for which way camera is rotated self.cameraYaw = 0 self.cameraPitch = 0 # Set up the camera self.disableMouse() # should probably clean up these magic numbers self.camera.setPos(20, 20, 20) # Create some lighting # this is a part that is completely unchanged from demo ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((-5, -5, -5)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) render.setLight(render.attachNewNode(directionalLight)) def setUpCarCollider(self): self.carCollideTrav = CollisionTraverser() base.cTrav = self.carCollideTrav self.handler = CollisionHandlerQueue() self.carRay = CollisionRay(self.carPositionX, self.carPositionY, self.carPositionZ, 0, 0, -1) self.carCollision = CollisionNode("groundCollision") self.carCollision.addSolid(self.carRay) # add to .egg <Scalar> collide-mask { 0x0 } #self.carCollision.setFromCollideMask(CollideMask.bit(0)) #self.carCollision.setIntoCollideMask(CollideMask.allOff()) self.carCollisionNode = self.car.attachNewNode(self.carCollision) self.carCollideTrav.addCollider(self.carCollisionNode, self.handler) self.carCollisionNode.show() def setUpMouseCollider(self): # clicking on objects stuff came from here: # https://www.panda3d.org/manual/index.php/Collision_Traversers # https://www.panda3d.org/manual/index.php/Collision_Handlers # will not use the traverser set up by car because slow # instead we will render each time clicked self.mouseCollideTrav = CollisionTraverser("mouseTraverse") self.mousehandler = CollisionHandlerQueue() # edit this so that from Object is the camera # self.mouseCollideTrav.addCollider(fromObject, queue) # self.mouseCollideTrav.traverse(render) # this next part came from: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects pickerNode = CollisionNode("mouseRay") pickerNp = camera.attachNewNode(pickerNode) pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.pickerRay = CollisionRay() pickerNode.addSolid(self.pickerRay) self.mouseCollideTrav.addCollider(pickerNp, self.mousehandler) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value def move(self, task): if(self.mode==self.modeFly): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() if self.keyMap["switch-mode"] and not self.hasSwitched: self.mode = (self.mode+1)%2 self.destroyInstructions() self.setUpDrivingInstructions() self.hasSwitched = True elif not self.keyMap["switch-mode"]: self.hasSwitched = False # the angle is in degrees with 360 equal to full rotation angleAdjustment = 100 if self.keyMap["cam-left"]: self.cameraYaw += angleAdjustment*dt if self.keyMap["cam-right"]: self.cameraYaw -= angleAdjustment*dt if self.keyMap["cam-up"]: self.cameraPitch += angleAdjustment*dt if self.keyMap["cam-down"]: self.cameraPitch -= angleAdjustment*dt positionAdjustment = 500 # should switch rad and Deg in variable name radToDeg = math.pi/180 # the x and y component of left and right moves, do not need to # compensate in z axis because not doing any roll, so there should be # no zComponent xComponent = math.cos(self.cameraYaw*radToDeg) yComponent = math.sin(self.cameraYaw*radToDeg) if self.keyMap["left"]: self.cameraPositionX -= positionAdjustment * dt *xComponent self.cameraPositionY -= positionAdjustment * dt *yComponent if self.keyMap["right"]: self.cameraPositionX += positionAdjustment * dt*xComponent self.cameraPositionY += positionAdjustment * dt*yComponent # for going forward, the orientation is rotated 90 degrees so need to # change components xComponent = math.cos(self.cameraYaw*radToDeg+math.pi/2)*math.cos( self.cameraPitch*radToDeg) yComponent = math.sin(self.cameraYaw*radToDeg+math.pi/2)*math.cos( self.cameraPitch*radToDeg) zComponent = math.sin(self.cameraPitch*radToDeg) if self.keyMap["forward"]: self.cameraPositionX += positionAdjustment * dt*xComponent self.cameraPositionY += positionAdjustment * dt*yComponent self.cameraPositionZ += positionAdjustment * dt*zComponent if self.keyMap["backward"]: self.cameraPositionX -= positionAdjustment * dt*xComponent self.cameraPositionY -= positionAdjustment * dt*yComponent self.cameraPositionZ -= positionAdjustment * dt*zComponent self.camera.setX(self.cameraPositionX) self.camera.setY(self.cameraPositionY) self.camera.setZ(self.cameraPositionZ) self.camera.setHpr(self.cameraYaw, self.cameraPitch, 0) # when implementing the use of the mouse look here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # clicking on 3D objects comes from here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # checks if it needs to add any objects: if(self.keyMap["mouse-click"]): # found way to speed this up by only doing collision check # when mouse clicked by not using cTrav like in this method # the way I did it I found here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects self.mouseCollideTrav.traverse(render) if(base.mouseWatcherNode.hasMouse()): mousePos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mousePos.getX(), mousePos.getY()) if(self.mousehandler.getNumEntries()>0): entries = list(self.mousehandler.getEntries()) # pathagorean formula for sorting entries.sort(key=lambda x: ((x.getSurfacePoint(render).getX( )-self.cameraPositionX)**2 + (x.getSurfacePoint(render).getY()- self.cameraPositionY)**2)**.5) newX = entries[0].getSurfacePoint(render).getX() newY = entries[0].getSurfacePoint(render).getY() newZ = entries[0].getSurfacePoint(render).getZ() self.objects.append(loader.loadModel("TestCar.egg")) self.objects[len(self.objects)-1].reparentTo(render) self.objects[len(self.objects)-1].setPos(newX, newY, newZ) return task.cont elif(self.mode == self.modeRace): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() degreeAdjustment = 60 positionAdjustment = 100 # should switch rad and Deg in variable name radToDeg = math.pi/180 # the x and y component of left and right moves, do not need to # compensate in z axis because not doing any roll, so there should be # no zComponent xComponent = math.sin(self.carYaw*radToDeg) yComponent = math.cos(self.carYaw*radToDeg) if self.keyMap["switch-mode"] and not self.hasSwitched: self.mode = (self.mode+1)%2 self.destroyInstructions() self.setUpFlyingInstructions() self.hasSwitched = True elif not self.keyMap["switch-mode"]: self.hasSwitched = False if self.keyMap["left"]: self.carYaw += degreeAdjustment * dt if self.keyMap["right"]: self.carYaw -= degreeAdjustment * dt if self.keyMap["forward"]: self.carPositionX -= positionAdjustment * dt*xComponent self.carPositionY += positionAdjustment * dt*yComponent if self.keyMap["backward"]: self.carPositionX += positionAdjustment * dt*xComponent self.carPositionY -= positionAdjustment * dt*yComponent # need to consider both the x and y component of offset for both # because x slowly changes to y as it turns actualXPos = (self.carPositionX+self.adjustedXForCenter* math.cos(radToDeg*self.carYaw)+self.adjustedYForCenter *math.sin(radToDeg*self.carYaw)) actualYPos = (self.carPositionY+self.adjustedYForCenter* math.cos(radToDeg*self.carYaw)+self.adjustedXForCenter *math.sin(radToDeg*self.carYaw)) self.car.setX(actualXPos) self.car.setY(actualYPos) self.car.setZ(self.carPositionZ) self.car.setHpr(self.carYaw, self.carPitch, 0) # when implementing the use of the mouse look here: # https://www.panda3d.org/manual/index.php/Clicking_on_3D_Objects # almost directly taken from ralph example entries = list(self.handler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) # worry about which thing it collides with later if (len(entries) > 0): # and entries[0].getIntoNode().getName() == "mountainCollide": self.carPositionZ = (entries[0].getSurfacePoint(render).getZ()) else: # because at 100 everything should be below car and do not want # to continually go up or else it may go up forever. self.car.setZ(100) print("less") # Modify view of camera so that it is behind car distanceBehind = 40 distanceAbove = 20 self.camera.setHpr(self.carYaw, -.5, 0) camX = actualXPos + distanceBehind * math.sin(radToDeg*self.carYaw) camY = actualYPos - distanceBehind * math.cos(radToDeg*self.carYaw) camZ = self.carPositionZ + distanceAbove self.camera.setPos(camX, camY, camZ) return task.cont
class MouseHandler (DirectObject): def __init__(self): self.accept('mouse1', self.onMouse1Down, []) # Left click self.accept('mouse1-up', self.onMouse1Up, []) self.accept('mouse3', self.onMouse3Down, []) # Right click self.showCollisions = False pickerNode = CollisionNode('mouseRay') pickerNP = camera.attachNewNode(pickerNode) pickerNode.setFromCollideMask(cardBuilder.cardCollisionMask) self.pickerRay = CollisionRay() pickerNode.addSolid(self.pickerRay) base.cTrav.addCollider(pickerNP, base.handler) if self.showCollisions: base.cTrav.showCollisions(render) base.disableMouse() self.activeCard = None self._targeting = False self._dragging = None # Counts down between clicks to detect double click self.doubleClickTimer = -1.0 self.doubleClickInterval = 1.0 self.line = attackLine.Line() @property def targeting(self): return self._targeting @targeting.setter def targeting(self, value): self._targeting = value if self._targeting: base.guiScene.showTargeting() else: base.guiScene.hideTargeting() def startTargeting(self, targetDesc, callback=None): base.targetDesc = targetDesc self.targeting = True if callback is not None: base.targetCallback = callback def getObjectClickedOn(self): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) base.cTrav.traverse(render) if (base.handler.getNumEntries() > 0): base.handler.sortEntries() pickedObj = base.handler.getEntry(0).getIntoNodePath() pickedObj = pickedObj.findNetPythonTag('zone') if pickedObj == self.dragging and base.handler.getNumEntries() > 1: for entry in base.handler.getEntries(): n = entry.getIntoNodePath().findNetPythonTag('zone') if n != self.dragging and n.parent != self.dragging: return n # If we don't find another object, do nothing # TODO: Every object should only have 1 collider to make this easier return pickedObj def doClick(self): pickedObj = self.getObjectClickedOn() if self.dragging: return if self.targeting and pickedObj is not None: base.targetCallback(pickedObj) elif pickedObj and not pickedObj.isEmpty(): zone = pickedObj.getPythonTag('zone') if zone is base.player.hand and not base.gameState.hasMulliganed: c = pickedObj.getPythonTag('card') if c in base.toMulligan: base.toMulligan.remove(c) base.audioMaster.mulliganSelectSound.play() else: base.toMulligan.append(c) base.audioMaster.mulliganDeselectSound.play() base.zoneMaker.makeMulliganHand() elif zone is base.player.hand: self.dragging = pickedObj elif zone is base.player.facedowns: c = pickedObj.getPythonTag('card') if self.activeCard: self.activeCard = None elif c.requiresTarget: self.startTargeting(c.targetDesc) def callback(target): base.revealFacedown(pickedObj, target) base.finishTargeting() base.targetCallback = callback else: base.revealFacedown(pickedObj) elif zone is base.player.faceups: c = pickedObj.getPythonTag('card') if not c.hasAttacked: self.activeCard = pickedObj elif zone is base.enemy.facedowns and self.activeCard: base.attack(self.activeCard, pickedObj) self.activeCard = None elif zone == base.enemy.faceups and self.activeCard: base.attack(self.activeCard, pickedObj) self.activeCard = None elif zone is base.enemy.face and self.activeCard: base.attack(self.activeCard, pickedObj) self.activeCard = None else: self.activeCard = None @property def dragging(self): return self._dragging @dragging.setter def dragging(self, obj): if obj is None: self._dragging.removeNode() base.zoneMaker.makePlayerHand() # Put the card back else: obj.reparentTo(base.zoneMaker.scene) obj.setHpr(0, -90, 0) self._dragging = obj def stopDragging(self): """ Stop dragging the card and play it if it's in the drop zone """ # Borders of the drop zone # If you drop the card outside the drop zone, # the action is cancelled pos = self._dragging.getPos() if inDropZone(pos): try: target = None c = self.dragging.getPythonTag('card') if c.fast: pickedObj = self.getObjectClickedOn() if pickedObj is not None and pickedObj != self.dragging: target = pickedObj elif c.requiresTarget: # don't fizzle if no valid target self.dragging = None return base.playCard(self._dragging, target) except IllegalMoveError as e: print(e) self.dragging = None def onMouse1Down(self): if self._dragging is not None: self.stopDragging() self.doubleClickTimer = -1 elif self.doubleClickTimer <= 0: self.doubleClickTimer = 0.2 try: self.doClick() except IllegalMoveError as e: print(e) def onMouse1Up(self): if self._dragging and self.doubleClickTimer <= 0: self.stopDragging() def onMouse3Down(self): if self.targeting: base.targetCallback(None) if self.dragging: self.dragging = None def mouseToXYPlane(self): mpos = base.mouseWatcherNode.getMouse() # See the Panda3d chess example self.pickerRay.setFromLens( base.camNode, mpos.getX(), mpos.getY()) # Get a vector relative to the camera position nearPoint = render.getRelativePoint( camera, self.pickerRay.getOrigin()) nearVec = render.getRelativeVector( camera, self.pickerRay.getDirection()) return PointAtZ(.5, nearPoint, nearVec) def mouseOverTask(self): if base.mouseWatcherNode.hasMouse(): if self.doubleClickTimer > 0: # Count down based on how long it took to draw the last frame self.doubleClickTimer -= globalClock.getDt() if hasattr(self, '_activeObj') and self._activeObj is not None: zone = self._activeObj.getPythonTag('card').zone if zone is base.player.facedowns or zone is base.enemy.facedowns: zoneMaker.hideCard(self._activeObj) base.zoneMaker.unfocusCard() self._activeObj = None if self.dragging is not None: # Drag the card in the XY plane self.dragging.setPos(self.mouseToXYPlane()) elif base.gameState.hasMulliganed: pickedObj = self.getObjectClickedOn() if pickedObj: card = pickedObj.getPythonTag('card') if card is not None and not pickedObj.getPythonTag('disableFocus'): self._activeObj = pickedObj if card.zone in ( base.enemy.hand, base.player.facedowns, base.enemy.facedowns): zoneMaker.showCard(pickedObj) base.zoneMaker.focusCard(pickedObj) if self.activeCard: basePos = (pickedObj.getPos(base.render) if pickedObj is not None and not pickedObj.isEmpty() else self.mouseToXYPlane()), self.line.draw( start=self.activeCard.getPos(base.render), end=basePos) else: self.line.clear()
class MousePicker(object): def __init__(self, pickTag='MyPickingTag', nodeName='pickRay', showCollisions=False): self.pickTag = pickTag self.nodeName = nodeName self.showCollisions = showCollisions def create(self): self.mPickerTraverser = CollisionTraverser() self.mCollisionQue = CollisionHandlerQueue() self.mPickRay = CollisionRay() self.mPickRay.setOrigin(base.camera.getPos(base.render)) self.mPickRay.setDirection( base.render.getRelativeVector(base.camera, Vec3(0, 1, 0))) #create our collison Node to hold the ray self.mPickNode = CollisionNode(self.nodeName) self.mPickNode.addSolid(self.mPickRay) #Attach that node to the camera since the ray will need to be positioned #relative to it, returns a new nodepath #well use the default geometry mask #this is inefficent but its for mouse picking only self.mPickNP = base.camera.attachNewNode(self.mPickNode) #we'll use what panda calls the "from" node. This is reall a silly convention #but from nodes are nodes that are active, while into nodes are usually passive environments #this isnt a hard rule, but following it usually reduces processing #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it, we use bitmasks to determine what we check other objects against #if they dont have a bitmask for bit 1 well skip them! self.mPickNode.setFromCollideMask(BitMask32(1)) #Register the ray as something that can cause collisions self.mPickerTraverser.addCollider(self.mPickNP, self.mCollisionQue) #Setup 2D picker self.mPickerTraverser2D = CollisionTraverser() self.mCollisionQue2D = CollisionHandlerQueue() self.mPickNode2D = CollisionNode('2D PickNode') self.mPickNode2D.setFromCollideMask(BitMask32(1)) self.mPickNode2D.setIntoCollideMask(BitMask32.allOff()) self.mPick2DNP = base.camera2d.attachNewNode(self.mPickNode2D) self.mPickRay2D = CollisionRay() self.mPickNode2D.addSolid(self.mPickRay2D) self.mPickerTraverser2D.addCollider(self.mPick2DNP, self.mCollisionQue2D) if self.showCollisions: self.mPickerTraverser.showCollisions(base.render) self.mPickerTraverser2D.showCollisions(base.aspect2d) def mousePick(self, traverse=None, tag=None): #do we have a mouse if (base.mouseWatcherNode.hasMouse() == False): return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.mPickRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.mPickerTraverser.traverse(traverse) if (self.mCollisionQue.getNumEntries() > 0): self.mCollisionQue.sortEntries() for entry in self.mCollisionQue.getEntries(): pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.render) return pickedObj, pos return None, None def mousePick2D(self, traverse=None, tag=None, all=False): #do we have a mouse if (base.mouseWatcherNode.hasMouse() == False): return None, None traverse = traverse or base.render tag = tag or self.pickTag mpos = base.mouseWatcherNode.getMouse() self.mPickRay2D.setFromLens(base.cam2d.node(), mpos.getX(), mpos.getY()) self.mPickerTraverser2D.traverse(base.aspect2d) if self.mCollisionQue2D.getNumEntries() > 0: self.mCollisionQue2D.sortEntries() if all: return [(entry.getIntoNodePath().findNetTag(tag), entry.getSurfacePoint(base.aspect2d)) for entry in self.mCollisionQue2D.getEntries()], None else: entry = self.mCollisionQue2D.getEntry(0) pickedObj = entry.getIntoNodePath() pickedObj = pickedObj.findNetTag(tag) if not pickedObj.isEmpty(): pos = entry.getSurfacePoint(base.aspect2d) return pickedObj, pos return None, None
class HexGrid(object): def __init__(self, w, h, show): self.width = w self.height = h self.loader = show.loader self.root = show.render.attachNewNode("hexgrid_root") self.base_tile = 'hex' self.diameter = 20 self.inscribed = self.diameter * sqrt(3) / 2 self.nodes = None self.show = show # Since we are using collision detection to do picking, we set it up like # any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() # Make a traverser self.picker_queue = CollisionHandlerQueue() # Make a handler # Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be positioned # relative to it self.pickerNP = show.camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other # collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() # Make our ray # Add it to the collision node self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.picker_queue) self.assemble() self.onclick_fn = None # self.mouseTask = self.taskMgr.add(self.mouseTask, 'mouseTask') # self.accept("mouse1", self.grabPiece) # left-click grabs a piece show.accept("mouse1-up", self.mouse_up) # releasing places it def mouse_up(self): # This task deals with the highlighting and dragging based on the mouse # Check to see if we can access the mouse. We need it to do anything # else if self.onclick_fn and self.show.mouseWatcherNode.hasMouse(): # get the mouse position mpos = self.show.mouseWatcherNode.getMouse() # Set the position of the ray based on the mouse position self.pickerRay.setFromLens(self.show.camNode, mpos.getX(), mpos.getY()) # Do the actual collision pass self.picker.traverse(self.root) if self.picker_queue.getNumEntries() > 0: # if we have hit something, sort the hits so that the closest # is first, and highlight that node self.picker_queue.sortEntries() i = int(self.picker_queue.getEntry(0).getIntoNode().getTag('hexmesh_i')) self.onclick_fn(self.nodes[i]) return Task.cont def onclick(self, fn): self.onclick_fn = fn def index_of(self, x, y): return x + y * self.width def xyi_iter(self, fn): for x in range(self.height): for y in range(self.width): fn(x, y, self.index_of(x, y)) def node_iter(self, fn): for node in self.nodes: fn(node) def assemble(self): random.seed(3141592) def make_node(x, y, i): row_offset = self.inscribed / 2. if y % 2 == 0 else 0 pos = Point3(x * self.inscribed + row_offset, y * self.diameter * 0.75, 0) self.nodes[i] = HexNode(self.base_tile, pos, self.root, i) self.nodes = [None for _ in range(self.width * self.height)] self.xyi_iter(make_node)
class GameEngine( DirectObject ): def __init__(self): print 'Game Engine Started' self.lastBlock = 0 self.map = [] self.startBlock = 0 self.endBlock = 0 self.mapName = 'test2' self.loadEnvironment(self.mapName) self.loadCursorPicker() self.camera = RTS_Camera() self.loadSimpleLighting() self.waveController = WaveController(self.mapName, self.startBlock, self.map) self.towerController = TowerController(self.waveController) self.setupKeyListeners() self.EFT = taskMgr.add(self.everyFrameTask, "everyFrameTask") # The task that is run every frame def everyFrameTask(self, task): self.camera.handleMouseInput() self.checkCursorCollision() self.towerController.updateTowers() return task.cont # Loads the level from the text file. Puts each # row into an array then creates the rows in the # 1st quadrant def loadEnvironment(self, file): fileName = 'maps/' + file + '.map' rows = [] self.environmentRoot = render.attachNewNode('environmentRoot') FILE = open(fileName, 'r') while( 1 ): line = FILE.readline() if( not line ): break print line, if( line[-1] == '\n' ): line = line[:-1] rows.append(line) rows.reverse() for i, row in enumerate(rows): self.createRow( i, row ) # Loads the models corresponding to the # characters in the map file for an entire row def createRow(self, rowIndex, row): mapRow = [] for colIndex, block in enumerate(row): block = BLOCK_CHAR_TO_MODEL[block]( self.environmentRoot, colIndex + 0.5, rowIndex + 0.5 ) mapRow.append( block ) block.setIndex( str(rowIndex) + ' ' + str(colIndex) ) if( block.isType(StartBlock) ): self.startBlock = block elif( block.isType(EndBlock) ): self.endBlock = block self.map.append(mapRow) # Creates necessary collision parts to determine what object # the cursor is hovering def loadCursorPicker(self): self.picker = CollisionTraverser() self.pq = CollisionHandlerQueue() self.pickerNode = CollisionNode('mouseRay') self.pickerNP = camera.attachNewNode(self.pickerNode) self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerNode.addSolid(self.pickerRay) self.picker.addCollider(self.pickerNP, self.pq) def checkCursorCollision(self): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens( base.camNode, mpos.getX(), mpos.getY() ) self.picker.traverse( self.environmentRoot ) if( self.pq.getNumEntries() > 0 ): self.pq.sortEntries() (row, ignore, col) = self.pq.getEntry(0).getIntoNode().getTag('index').partition(' ') row = int(row) col = int(col) block = self.map[row][col] if( block != self.lastBlock ): block.highlight() if( self.lastBlock ): self.lastBlock.unHighlight() self.lastBlock = block else: if( self.lastBlock ): self.lastBlock.unHighlight() self.lastBlock = 0 def mouseClick(self): if( self.lastBlock ): self.towerController.addTower(self.lastBlock) def spawnEnemy(self): e = Enemy(self.startBlock, self.map) e.moveToEnd() def setTowerType(self, type): self.towerController.currentTowerType = type def setupKeyListeners(self): self.accept('mouse1', self.mouseClick) self.accept('q', self.waveController.start) self.accept('1', self.setTowerType, [NormalTower]) self.accept('2', self.setTowerType, [SlowTower]) self.accept('3', self.setTowerType, [StunTower]) def loadSimpleLighting(self): ambientLight = AmbientLight( "ambientLight" ) ambientLight.setColor( Vec4(.8, .8, .8, 1) ) directionalLight = DirectionalLight( "directionalLight" ) directionalLight.setDirection( Vec3( 0, 45, -45 ) ) directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 0.6 ) ) render.setLight(render.attachNewNode( directionalLight ) ) render.setLight(render.attachNewNode( ambientLight ) )
class MyApp(ShowBase, DirectObject.DirectObject): def __init__(self): ShowBase.__init__(self) # generate a new game game = Game() game.create_player('Player One') game.create_player('Player Two') game.create_player('Player Three') game.initialize_board() # place some random cities for player in game.players.values(): # give the player some random resources for resource in player.resources: player.resources[resource] = random.randint(0,8) while True: n = random.choice(game.board.network.nodes()) if game.board.node_available(n): game.board.update_building(n, player, 'city') # place a random road m = random.choice(game.board.network.neighbors(n)) game.board.network.edge[n][m]['road'] = True game.board.network.edge[n][m]['player'] = player break self.board_renderer = BoardRenderer(self, game.board) self.hand_renderer = HandRenderer(self, game.players.values()[0]) # setup some 3-point lighting for the whole board lKey = DirectionalLight('lKey') lKey.setColor(VBase4(0.9,0.9,0.9,1)) lKeyNode = render.attachNewNode(lKey) lKeyNode.setH(-63) lKeyNode.setP(-60) lKeyNode.setR(-30) render.setLight(lKeyNode) lFill = DirectionalLight('lFill') lFill.setColor(VBase4(0.4,0.4,0.4,1)) lFillNode = render.attachNewNode(lFill) lFillNode.setH(27) lFillNode.setP(-15) lFillNode.setR(-30) render.setLight(lFillNode) lBack = DirectionalLight('lBack') lBack.setColor(VBase4(0.3,0.3,0.3,1)) lBackNode = render.attachNewNode(lBack) lBackNode.setH(177) lBackNode.setP(-20) lBackNode.setR(0) render.setLight(lBackNode) lBelow = DirectionalLight('lBelow') lBelow.setColor(VBase4(0.4,0.4,0.4,1)) lBelowNode = render.attachNewNode(lBelow) lBelowNode.setH(0) lBelowNode.setP(90) lBelowNode.setR(0) render.setLight(lBelowNode) self.accept('a', self.on_toggle_anti_alias) self.mouse_controlled = True self.on_toggle_mouse_control() self.accept('m', self.on_toggle_mouse_control) self.accept('q', self.on_quit) # onto-board selection collision test select_mask = BitMask32(0x100) self.select_ray = CollisionRay() select_node = CollisionNode('mouseToSurfaceRay') select_node.setFromCollideMask(select_mask) select_node.addSolid(self.select_ray) select_np = self.camera.attachNewNode(select_node) self.select_queue = CollisionHandlerQueue() self.select_traverser = CollisionTraverser() self.select_traverser.addCollider(select_np, self.select_queue) # create a plane that only collides with the mouse ray select_plane = CollisionPlane(Plane(Vec3(0,0,1), Point3(0,0,0))) # add plane to render self.select_node = CollisionNode('boardCollisionPlane') self.select_node.setCollideMask(select_mask) self.select_node.addSolid(select_plane) self.select_plane_np = self.render.attachNewNode(self.select_node) self.debug_select = draw_debugging_arrow(self, Vec3(0,0,0), Vec3(0,1,0)) self.taskMgr.add(self.update_mouse_target, "mouseTarget") self.taskMgr.add(self.update_debug_arrow, "updateDebugArrow") def on_toggle_anti_alias(self): if AntialiasAttrib.MNone != render.getAntialias(): render.setAntialias(AntialiasAttrib.MNone) print "anti-aliasing disabled" else: render.setAntialias(AntialiasAttrib.MAuto) print "anti-aliasing enabled" def on_toggle_mouse_control(self): if self.mouse_controlled: self.disableMouse() self.taskMgr.add(self.spin_camera_task, "spinCameraTask") else: self.enableMouse() self.mouse_controlled = not self.mouse_controlled def spin_camera_task(self, task): height = 9 distance = 15 speed = 1./16 angle = (task.time*speed) * 2 * pi self.camera.setPos(distance*cos(angle), distance*-sin(angle), height) self.camera.lookAt(0,0,0) if self.mouse_controlled: return Task.done return Task.cont def update_mouse_target(self, task): if not base.mouseWatcherNode.hasMouse(): self.mouse_target = None return Task.cont # setup ray through camera position and mouse position (on camera plane) mouse_pos = base.mouseWatcherNode.getMouse() self.select_ray.setFromLens(self.board_renderer.base.camNode, mouse_pos.getX(), mouse_pos.getY()) self.select_traverser.traverse(self.board_renderer.base.render) # abort if there's no collision if not self.select_queue.getNumEntries(): return Task.cont collision = self.select_queue.getEntry(0) self.mouse_board_collision = collision.getSurfacePoint(collision.getIntoNodePath()) self.mouse_target = 'board' return Task.cont def update_debug_arrow(self, task): if self.mouse_target: self.debug_select.setPos(self.mouse_board_collision) return Task.cont def on_pick(self): if not self._update_pick_ray(): return # traverse scene graph and determine nearest selection (if pickable) self.pick_traverser.traverse(self.board_renderer.base.render) self.pick_queue.sortEntries() if not self.pick_queue.getNumEntries(): return node = self.pick_queue.getEntry(0).getIntoNodePath().findNetTag('pickable') if node.isEmpty() or node.getTag('pickable') == 'False': return # add some color ts = TextureStage('ts') ts.setMode(TextureStage.MModulate) colors = list(Game.player_colors) colors.remove('white') node.setTexture(ts, self.board_renderer.tileset.load_texture('textures/player%s.png' % random.choice(colors).capitalize())) def on_quit(self): sys.exit(0)
class GameApp: def __init__(self): self.gameEventHandler = GameEventHandler(self) self.camLimits = ((-5, 5), (-6.5, 5), (1, 10)) self.gameReady = False self.hovered = None self.clicked = None self.modelToFigure = {} self.modelToField = {} self.modelToSeaField = {} self.modelToBuildingField = {} self.modelToBuilding = {} # self.highlightableObjects = render.attachNewNode('highlightables') self.setupColisionForHighlight() self.songMenu = None self.buildMenu = None def setupColisionForHighlight(self): # Since we are using collision detection to do picking, we set it up like # any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() # Make a traverser self.pq = CollisionHandlerQueue() # Make a handler # Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') # Attach that node to the camera since the ray will need to be positioned # relative to it self.pickerNP = self.camera.attachNewNode(self.pickerNode) # Everything to be picked will use bit 1. This way if we were doing other # collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() # Make our ray # Add it to the collision node self.pickerNode.addSolid(self.pickerRay) # Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) def getCameraCoords(self): return self.camera.getPos() def setCameraCoords(self, x, y, z): self.camera.setPos(x, y, z) def getMouseCoords(self): if self.mouseWatcherNode.hasMouse(): return self.mouseWatcherNode.getMouse() return None def drawIsland(self, island, suppressRot=False): island.model.setPos(island.pos[0], island.pos[1], 0.001) island.model.setScale(0.05, 0.05, 0.05) island.model.reparentTo(self.render) for f in range(0, 6): circle = self.loader.loadModel('models/circle') pos = (island.fields[f].x, island.fields[f].y, 0.4) circle.setPos(pos) circle.setScale(0.4) circle.reparentTo(island.model) circle.setTag('clickable', 'true') cs = CollisionSphere(0, 0, 0, 1) cnodePath = circle.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) island.fields[f].model = circle self.modelToField[circle.getKey()] = island.fields[f] for i in range(0, 3): buildingField = island.buildingFields[i] circle = self.loader.loadModel('models/circle') pos = (buildingField.x, buildingField.y, 0.1) circle.setPos(pos) circle.setScale(0.6) circle.reparentTo(island.model) circle.setTag('clickable', 'true') cs = CollisionSphere(0, 0, 0, 1) cnodePath = circle.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) buildingField.model = circle self.modelToBuildingField[circle.getKey()] = buildingField # put the bay field circle = self.loader.loadModel('models/circle') pos = (island.bay.x, island.bay.y, 0.2) circle.setPos(pos) circle.setScale(0.6) circle.reparentTo(island.model) circle.setTag('clickable', 'true') cs = CollisionSphere(0, 0, 0, 1) cnodePath = circle.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) island.bay.model = circle self.modelToSeaField[circle.getKey()] = island.bay degree = angle((0, 1), island.pos) * 180 / math.pi if island.pos[0] > 0: degree *= -1 if not suppressRot: island.model.setHpr(degree, 0, 0) def drawFigures(self): for player in self.game.players: for figure in player.figures: if hasattr(figure, 'model'): continue if type(figure) == Ship: field = figure.field figure.model = self.loader.loadModel('models/ship') figure.model.reparentTo(field.model) cs = CollisionSphere(1.5, 0, 1, 1.3) cnodePath = figure.model.attachNewNode( CollisionNode('cnode')) cnodePath.node().addSolid(cs) # cnodePath.show() cs = CollisionSphere(0, 0, 1.4, 1.3) cnodePath = figure.model.attachNewNode( CollisionNode('cnode')) cnodePath.node().addSolid(cs) # cnodePath.show() cs = CollisionSphere(-1.8, 0, 1, 1) cnodePath = figure.model.attachNewNode( CollisionNode('cnode')) cnodePath.node().addSolid(cs) # cnodePath.show() figure.model.setScale(1.4) figure.model.setHpr(90, 0, 0) # figure.model.setTag('highlightable', 'true') figure.model.setTag('clickable', 'true') self.modelToFigure[figure.model.getKey()] = figure else: field = figure.field figure.model = self.loader.loadModel('models/warrior100') figure.model.reparentTo(field.model) cs = CollisionSphere(0, -.35, 7, 1) cnodePath = figure.model.attachNewNode( CollisionNode('cnode')) cnodePath.node().addSolid(cs) figure.model.setScale(0.35) figure.model.setTag('highlightable', 'true') figure.model.setTag('clickable', 'true') self.modelToFigure[figure.model.getKey()] = figure col = 256 * int(player.color) # set figure title title = TextNode(str(figure.model.getKey()) + '_title') title.setText(type(figure).__name__) title.setCardColor(col, col, col, 1) title.setCardAsMargin(0.1, 0.1, 0.1, 0.1) title.setCardDecal(True) titleNode = self.render.attachNewNode(title) titleNode.reparentTo(figure.model) titleNode.setScale(3) titleNode.setPos(0, 3, 10) if type(figure) == Ship: titleNode.setScale(1.5) titleNode.setPos(-1.5, 0, 3) titleNode.setBillboardPointEye() def drawSeaways(self): for field in self.game.board.seawayFields: circle = self.loader.loadModel('models/circle') pos = (field.x, field.y, 0) circle.setPos(pos) circle.setScale(0.04) circle.setHpr(-90, 0, 0) circle.reparentTo(self.render) circle.setTag('clickable', 'true') cs = CollisionSphere(0, 0, 0, 1) cnodePath = circle.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) field.model = circle self.modelToSeaField[circle.getKey()] = field def drawBuilding(self, building, field): model = self.loader.loadModel('models/house') # model.setScale(0.05) model.reparentTo(field.model) building.model = model self.modelToBuilding[model.getKey()] = building player = self.game.currentPlayer() model.setTag('clickable', 'true') cs = CollisionSphere(0, 0, 0, 2) cnodePath = model.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) # cnodePath.show() col = 256 * int(player.color) # set building title title = TextNode(str(building.model.getKey()) + '_title') title.setText(building.building) title.setCardColor(col, col, col, 1) title.setCardAsMargin(0.1, 0.1, 0.1, 0.1) title.setCardDecal(True) titleNode = self.render.attachNewNode(title) titleNode.reparentTo(building.model) titleNode.setScale(1.5) titleNode.setPos(0, 0, 3) titleNode.setBillboardPointEye() def drawGame(self, game): if not self.gameReady: self.game = game # menu = OnscreenImage(image = 'textures/menu.png', pos = (1.53, 0, 0), scale=(0.35, 1, 1)) # menu.setTransparency(TransparencyAttrib.MAlpha) # setup the background of the board self.environ = self.loader.loadModel('models/plane') sea = self.loader.loadTexture('textures/sea.png') self.environ.setTexture(sea) self.environ.setPos(0, 1, 0) self.environ.setScale(1.1) sea.setWrapU(Texture.WM_repeat) sea.setWrapV(Texture.WM_repeat) self.environ.reparentTo(self.render) # setup camera self.camera.setPos(0, 0, 10) self.camera.setHpr(0, -70, 0) self.camLens.setNear(0.85) # setup lighting plight = PointLight('plight') plight.setColor(VBase4(1, 1, 1, 3)) plnp = self.render.attachNewNode(plight) plnp.setPos(10, 0, 10) self.render.setLight(plnp) ambientLight = AmbientLight('ambientLight') ambientLight.setColor(Vec4(0.25, 0.25, 0.25, .3)) ambientLightNP = self.render.attachNewNode(ambientLight) self.render.setLight(ambientLightNP) # place islands first = True for island in game.board.islands: island.drawable = True island.model = self.loader.loadModel('models/island2_104') self.drawIsland(island, first) first = False self.drawFigures() self.drawSeaways() self.turn = OnscreenText(text='Black\'s turn.', pos=(0.06, -0.1), align=TextNode.ALeft, parent=base.a2dTopLeft, scale=0.06) self.resources = OnscreenText(text='Resources: ', pos=(0.08, -0.2), align=TextNode.ALeft, parent=base.a2dTopLeft, scale=0.06) self.gameReady = True player = 'Black' if game.turn == 1: player = 'White' self.turn.setText(player + '\'s turn.') resourcesText = 'Resources: ' + \ str(self.game.currentPlayer().resources) self.resources.setText(resourcesText) if self.game.loosers != None: message = OnscreenText(text='End of the game', align=TextNode.ACenter, pos=(0, 0), scale=0.1) if self.game.loosers == 'black': message.setText('White wins!') elif self.game.loosers == 'white': message.setText('Black wins!') else: message.setText('Nobody wins!') def destroyGame(self): children = self.render.getChildren() for child in children: child.removeNode() def cameraSpeed(self, height, speedRange): # Figure out how 'wide' each range is leftSpan = self.camLimits[2][1] - self.camLimits[2][0] rightSpan = speedRange[1] - speedRange[0] # Convert the left range into a 0-1 range (float) valueScaled = float(height - self.camLimits[2][0]) / float(leftSpan) # Convert the 0-1 range into a value in the right range. return speedRange[0] + (valueScaled * rightSpan) def moveCamera(self): mousePos = self.getMouseCoords() if mousePos == None: return x, y = mousePos camX, camY, camZ = self.getCameraCoords() transformX, transformY = 0, 0 speed = self.cameraSpeed(camZ, (0.01, 0.2)) if x < -0.7 and y < -0.7: transformX -= speed transformY -= speed elif x > 0.7 and y < -0.7: transformX += speed transformY -= speed elif x < -0.7 and y > 0.7: transformX -= speed transformY += speed elif x > 0.7 and y > 0.7: transformX += speed transformY += speed else: if x < -0.7: transformX -= speed elif x > 0.7: transformX += speed if y < -0.7: transformY -= speed elif y > 0.7: transformY += speed newX = camX + transformX newY = camY + transformY if newX < self.camLimits[0][0] or newX > self.camLimits[0][1]: newX = camX if newY < self.camLimits[1][0] or newY > self.camLimits[1][1]: newY = camY self.setCameraCoords(newX, newY, camZ) def highlight(self): if self.mouseWatcherNode.hasMouse(): mPos = self.mouseWatcherNode.getMouse() # Set the position of the ray based on the mouse position self.pickerRay.setFromLens(self.camNode, mPos.getX(), mPos.getY()) self.picker.traverse(self.render) if self.pq.getNumEntries() > 0: # This is so we get the closest object. self.pq.sortEntries() pickedObj = self.pq.getEntry(0).getIntoNodePath() # pick the model and not the cnode pickedObj = pickedObj.findNetTag('clickable') if not pickedObj.isEmpty(): return pickedObj @staticmethod def boardingTransformations(figure, pos): figure.model.setScale(0.2) figure.model.setPos(1 + pos, 0, 1) @staticmethod def unboardingTransformations(figure): figure.model.setScale(0.35) figure.model.setPos(0, 0, 0) def drawSongsMenu(self, songs, field): if self.songMenu: return self.songMenu = [(OnscreenText(text='Choose song:', pos=(-0.7, -0.1), align=TextNode.ALeft, parent=base.a2dTopRight, scale=0.06), field)] i = 1 for song in songs: item = OnscreenText(text=str(i) + ') Song of ' + song, pos=(-0.7, -0.1 - i * 0.1), align=TextNode.ALeft, parent=base.a2dTopRight, scale=0.06) i += 1 self.songMenu.append((item, song)) def destroySongMenu(self): if self.songMenu: for item in self.songMenu: item[0].destroy() self.songMenu = None def drawBuildMenu(self, buildings, field): self.buildMenu = [(OnscreenText(text='Choose building:', pos=(-0.7, -0.1), align=TextNode.ALeft, parent=base.a2dTopRight, scale=0.06), field)] i = 1 for building in buildings: price = Building.buildingPrice(building) text = '{0}) {1} ({2})'.format(str(i), building, price) item = OnscreenText(text=text, pos=(-0.7, -0.1 - i * 0.1), align=TextNode.ALeft, parent=base.a2dTopRight, scale=0.06) i += 1 self.buildMenu.append((item, building)) def destroyBuildMenu(self): if self.buildMenu: for item in self.buildMenu: item[0].destroy() self.buildMenu = None def clickFigure(self, figure): print('figure') if type(figure) == Ship: ship = figure figure = self.clicked if isinstance(figure, Figure) and type(figure) != Ship: if figure.hasMoved: self.clicked = ship return if (figure.field.island == ship.field.island and figure.player == ship.player and None in ship.fields): figure.field.put(None) pos = 0 if ship.fields[0] == None: ship.fields[0] = figure else: ship.fields[1] = figure pos = 1 figure.field = ship figure.model.reparentTo(ship.model) self.boardingTransformations(figure, pos) figure.hasMoved = True if ship.player == self.game.currentPlayer(): self.clicked = ship else: if figure.player == self.game.currentPlayer(): self.clicked = figure def initiateBattle(self, blackTroops, whiteTroops): self.blackTroops = blackTroops self.whiteTroops = whiteTroops battle = self.game.newBattle() self.push(battle) self.gameEventHandler.ignoreAll() def clickField(self, field): print(self.clicked) if type(self.clicked) == Ship: return if self.clicked and isinstance(self.clicked, Figure): figure = self.clicked board = self.game.board if figure.hasMoved: return if type(figure.field) == Ship: if field in board.possibleMoves(figure.field): figure.field.removeFigure(figure) player = self.game.currentPlayer() battle = field.figure != None and field.figure.player != player whiteTroops = field.figure field.put(figure) figure.model.reparentTo(field.model) self.unboardingTransformations(figure) figure.hasMoved = True self.initiateBattle(figure, whiteTroops) if field in board.possibleMoves(self.clicked): initiateBattle = field.figure != None whiteTroops = field.figure figure.field.put(None) field.put(figure) figure.model.reparentTo(field.model) figure.hasMoved = True if initiateBattle: self.initiateBattle(figure, whiteTroops) if isinstance(self.clicked, Building): building = self.clicked player = self.game.currentPlayer() if not field.figure or field.figure.player != player: if field.island != building.field.island: return figure = None battle = field.figure != None and field.figure.player != player whiteTroops = field.figure if building.building == 'House': figure = self.game.giveBirthToPeasant(field) elif building.building == 'Barracks': figure = self.game.giveBirthToWarrior(field) if figure == None: return if figure: self.drawFigures() if battle: self.initiateBattle(figure, whiteTroops) else: print('Not enough resources!') def clickSeaField(self, field): if type(self.clicked) == Ship: figure = self.clicked if figure.hasMoved: return if field.figure != None: return if field in figure.field.linked: figure.field.put(None) field.put(figure) figure.model.reparentTo(field.model) figure.hasMoved = True if type(self.clicked) == Building: player = self.game.currentPlayer() building = self.clicked if building.building == 'Harbor': if building.field.island.bay != field: return player = self.game.currentPlayer() if not field.figure or field.figure.player != player: ship = self.game.buildShip(field) if ship: self.drawFigures() else: print('Not enough resources') def clickBuildingField(self, field): print("Gonna build, huh?") player = self.game.currentPlayer() print(self.game.possibleBuildings(field.island)) self.drawSongsMenu(self.game.possibleSongs(field.island), field) def handle(self, event, *args): print(event) if type(self.current()) != Game: return if event == 'wheel_up': x, y, z = self.getCameraCoords() if z > self.camLimits[2][0]: self.setCameraCoords(x, y, z - 1) elif event == 'wheel_down': x, y, z = self.getCameraCoords() if z < self.camLimits[2][1]: self.setCameraCoords(x, y, z + 1) elif event == 'enter': if self.game.loosers != None: self.pop() self.game.changeTurn() elif event == 'left_click': obj = self.highlight() if obj != None: key = obj.getKey() # if it's a figure if key in self.modelToFigure: figure = self.modelToFigure[key] self.clickFigure(figure) # if it's a figure field if key in self.modelToField: field = self.modelToField[key] self.clickField(field) # if it's a building field if key in self.modelToBuildingField: field = self.modelToBuildingField[key] self.clickBuildingField(field) # if it's a sea field if key in self.modelToSeaField: field = self.modelToSeaField[key] self.clickSeaField(field) # if it's a building if key in self.modelToBuilding: self.clicked = self.modelToBuilding[key] else: self.clicked = None if self.songMenu != None: if obj == None or obj.getKey() not in self.modelToBuildingField: self.destroySongMenu() elif event in [str(i) for i in range(1, 10)]: if self.songMenu: if self.songMenu[0][1] != self.game.board.islands[0]: song = self.songMenu[int(event)][1] buildings = self.game.buildings[song] self.drawBuildMenu(buildings, self.songMenu[0][1]) else: print('CHANGE OBJECTIVES!') self.destroySongMenu() return if self.buildMenu: building = self.buildMenu[int(event)][1] field = self.buildMenu[0][1] building = self.game.build(building, field) if building: self.drawBuilding(building, field) else: print('Not enough resources or field is taken!!>@') self.destroyBuildMenu() def hoverFigure(self, hovered): if self.hovered != None: reverseFactor = self.hovered.getScale()[0] reverseFactor *= REVERSE_HIGHLIGHT_SCALE self.hovered.setScale(reverseFactor) self.hovered = None if hovered != None: figure = self.modelToFigure[hovered.getKey()] if figure.player.color != str(self.current().turn): return self.hovered = hovered factor = HIGHLIGHT_SCALE * hovered.getScale()[0] hovered.setScale(factor)
class World(DirectObject): def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking", style=1, fg=(1, 1, 1, 1), pos=(0.8, -0.95), scale=.07) self.escapeEvent = OnscreenText( text="ESC: Quit", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale=.05) self.mouse1Event = OnscreenText( text="Left-click and drag: Pick up and drag piece", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.90), align=TextNode.ALeft, scale=.05) self.accept('escape', sys.exit) #Escape quits base.disableMouse() #Disble mouse camera control camera.setPosHpr(0, -13.75, 6, 0, -25, 0) #Set the camera self.setupLights() #Setup default lighting #Since we are using collision detection to do picking, we set it up like #any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() #Make a traverser self.pq = CollisionHandlerQueue() #Make a handler #Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') #Attach that node to the camera since the ray will need to be positioned #relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() #Make our ray self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node #Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) #Now we create the chess board and its pieces #We will attach all of the squares to their own root. This way we can do the #collision pass just on the sqaures and save the time of checking the rest #of the scene self.squareRoot = render.attachNewNode("squareRoot") #For each square self.squares = [None for i in range(64)] self.pieces = dict((i, None) for i in range(64)) #MOD for i in range(64): #Load, parent, color, and position the model (a single square polygon) self.squares[i] = loader.loadModel("models/square") self.squares[i].reparentTo(self.squareRoot) self.squares[i].setPos(SquarePos(i)) self.squares[i].setColor(SquareColor(i)) #Set the model itself to be collideable with the ray. If this model was #any more complex than a single polygon, you should set up a collision #sphere around it instead. But for single polygons this works fine. self.squares[i].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) #Set a tag on the square's node so we can look up what square this is #later during the collision pass self.squares[i].find("**/polygon").node().setTag('square', str(i)) #We will use this variable as a pointer to whatever piece is currently #in this square #The order of pieces on a chessboard from white's perspective. This list #contains the constructor functions for the piece classes defined below pieceOrder = (Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook) for i in range(8, 16): #Load the white pawns self.pieces[i] = Pawn(i, WHITE) for i in range(48, 56): #load the black pawns self.pieces[i] = Pawn(i, PIECEBLACK) for i in range(8): #Load the special pieces for the front row and color them white self.pieces[i] = pieceOrder[i](i, WHITE) #Load the special pieces for the back row and color them black self.pieces[i + 56] = pieceOrder[i](i + 56, PIECEBLACK) #This will represent the index of the currently highlited square self.hiSq = False #This wil represent the index of the square where currently dragged piece #was grabbed from self.dragging = False #Start the task that handles the picking self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') self.accept("mouse1", self.grabPiece) #left-click grabs a piece self.accept("mouse1-up", self.releasePiece) #releasing places it #This function swaps the positions of two pieces def swapPieces(self, fr, to): temp = self.pieces[fr] self.pieces[fr] = self.pieces[to] self.pieces[to] = temp if self.pieces[fr]: self.pieces[fr].square = fr self.pieces[fr].obj.setPos(SquarePos(fr)) if self.pieces[to]: self.pieces[to].square = to self.pieces[to].obj.setPos(SquarePos(to)) def mouseTask(self, task): #This task deals with the highlighting and dragging based on the mouse #First, clear the current highlight if self.hiSq is not False: self.squares[self.hiSq].setColor(SquareColor(self.hiSq)) self.hiSq = False #Check to see if we can access the mouse. We need it to do anything else if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #If we are dragging something, set the position of the object #to be at the appropriate point over the plane of the board if self.dragging is not False: #Gets the point described by pickerRay.getOrigin(), which is relative to #camera, relative instead to render nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) #Same thing with the direction of the ray nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection()) self.pieces[self.dragging].obj.setPos( PointAtZ(.5, nearPoint, nearVec)) #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.squareRoot) if self.pq.getNumEntries() > 0: #if we have hit something, sort the hits so that the closest #is first, and highlight that node self.pq.sortEntries() i = int(self.pq.getEntry(0).getIntoNode().getTag('square')) #Set the highlight on the picked square self.squares[i].setColor(HIGHLIGHT) self.hiSq = i return Task.cont def grabPiece(self): #If a square is highlighted and it has a piece, set it to dragging mode if (self.hiSq is not False and self.pieces[self.hiSq]): self.dragging = self.hiSq self.hiSq = False def releasePiece(self): #Letting go of a piece. If we are not on a square, return it to its original #position. Otherwise, swap it with the piece in the new square if self.dragging is not False: #Make sure we really are dragging something #We have let go of the piece, but we are not on a square if self.hiSq is False: self.pieces[self.dragging].obj.setPos( SquarePos(self.dragging)) else: #Otherwise, swap the pieces self.swapPieces(self.dragging, self.hiSq) #We are no longer dragging anything self.dragging = False def setupLights(self): #This function sets up some default lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(.8, .8, .8, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(0, 45, -45)) directionalLight.setColor(Vec4(0.2, 0.2, 0.2, 1)) render.setLight(render.attachNewNode(directionalLight)) render.setLight(render.attachNewNode(ambientLight))
class Picker(Viewer): """ View and click objects in a scene.""" def __init__(self): # Parent init. super(Picker, self).__init__() self.disableMouse() # Picker stuff. self.contact_margin = Vec3(0.01, 0.01, 0.01) self.parser = None self.marked = None self.attached_pairs = set() self.contacts = None self.contact_points = None self.contact_bottoms = None self.compound_components = [] self.compound_objects = [] self.joints = JointManager() self.wire_attrib = RenderModeAttrib.make(RenderModeAttrib.MWireframe, 4.) self.attachment_colors = (Vec4(0.1, 0.1, 1., 1.), Vec4(0.1, 0.8, 0.1, 1.), Vec4(1., 0.1, 1., 1.), Vec4(1., 0.5, 0.1, 1.), Vec4(1., 1., 1., 1.)) self.max_attach = 999 self.permanent_events += ["mouse1"] # Make cursor dot. self.cursor = self._build_cursor("cross") s = 0.08 self.cursor.setScale(s, s, s) self.cursor.setColor(1, 1, 1, 1) self.cursor.reparentTo(self.aspect2d) self.taskMgr.add(self.draw_cursor2d, "draw_cursor2d") self.permanent_tasks.append("draw_cursor2d") def init_ssos(self, *args, **kwargs): super(Picker, self).init_ssos(*args, **kwargs) def init_physics(self, *args, **kwargs): super(Picker, self).init_physics(*args, **kwargs) self.joints.bbase = self.bbase def init_picker(self): # Collision traverser self.traverser = CollisionTraverser("traverser") # Collision handler self.handler = CollisionHandlerQueue() # Initialize and set up picker ray node and NodePath self.picker = CollisionNode("mouse_ray") self.pickerNP = self.camera.attachNewNode(self.picker) self.picker.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.picker_ray = CollisionRay() self.picker.addSolid(self.picker_ray) self.traverser.addCollider(self.pickerNP, self.handler) mark_color = (1, 1, 1, 0.3) self.base_mark = self.create_mark(color=mark_color) connector_color = (1, 1, 1, 1) self.base_connector = self.create_connector(color=connector_color) def _build_cursor(self, shape="sphere"): if shape == "sphere": cursor = self._load("sphere.bam") elif shape == "cross": cursor = LineNodePath() lines = [[Point3(-0.5, 0, 0), Point3(0.5, 0, 0)], [Point3(0, 0, -0.5), Point3(0, 0, 0.5)]] cursor.drawLines(lines) cursor.setThickness(1) cursor.create() # cursor = NodePath("cross") # S = {"cylinderX.bam": ((0., 0., 0.), (1., 0.1, 0.1)), # "cylinderY.bam": ((0., 0., 0.), (0.1, 1., 0.1)), # "cylinderZ.bam": ((0., 0., 0.), (0.1, 0.1, 1.))} # for k, v in S.iteritems(): # m = self._load(k) # m.setName(k) # m.setPos(*v[0]) # m.setScale(*v[1]) # m.reparentTo(cursor) #BP() return cursor def create_mark(self, color): """ Makes a graphical mark object.""" # Make a graphical box. props = dict(name="mark", color=color, model="box-round.egg") obj = GSO(props=props) return obj def create_connector(self, color): """ Makes a graphical connector object.""" # Make a graphical box. props = dict(name="connector", color=color, model="connector.egg") obj = GSO(props=props) return obj def start_picker(self, pickables): # Set pickable objs. for i, obj in enumerate(pickables): obj.setTag("pickable", str(i)) # Add mouse events. self.accept("mouse1", self.clicked, extraArgs=[1]) # Start contact detector. detector = ContactDetector(self.bbase.world, self.scene, margin=self.contact_margin) self.contacts = detector.contacts self.contact_bodies = detector.bodies self.contact_points = detector.points parser = Parser(self.contacts, self.contact_bodies) self.contact_bottoms = parser.bottom_bodies self.connectors = {} def stop_picker(self): self.removeTask("mouse1") def goto_sso(self, *args, **kwargs): self.clear_attachments() self.stop_picker() super(Picker, self).goto_sso(*args, **kwargs) self.remove_physics() # Start picker. pickables = self.sso.descendants(type_=PSO) self.start_picker(pickables) self.attach_physics() def get_picked_obj(self): mpos = self.mouseWatcherNode.getMouse() self.picker_ray.setFromLens(self.cam.node(), mpos.getX(), mpos.getY()) self.traverser.traverse(self.render) if self.handler.getNumEntries() > 0: # This is so we get the closest object self.handler.sortEntries() entries = self.handler.getEntries() for entry in entries: picked_obj = entry.getIntoNodePath().findNetTag("pickable") if not picked_obj.isEmpty(): break if picked_obj.isEmpty(): picked_obj = None else: picked_obj = None return picked_obj def clicked(self, button): """ Mouse click handler.""" if self.mouseWatcherNode.hasMouse(): # Get picked object picked_obj = self.get_picked_obj() if picked_obj is not None: if self.marked is None: # New mark activated. self.marked = picked_obj self.show_marked(picked_obj, True) event = "mark" elif picked_obj == self.marked: # Existing mark deactivated. self.show_marked(picked_obj, False) self.marked = None event = "unmark" else: # New attachment or detachment. pair = tuple(sorted((self.marked, picked_obj))) ij = tuple( sorted((self.contact_bodies.index(pair[0]), self.contact_bodies.index(pair[1])))) if ij in self.contacts: f_add = (ij, pair) not in self.attached_pairs if (not f_add or len(self.attached_pairs) < self.max_attach): self.store_attachment(ij, pair, f_add) self.show_marked(self.marked, False) self.marked = None event = "attach" if f_add else "detach" else: print("Max attachments already reached.") event = "max-attach" else: event = "non-contact" else: event = "non-pick" return picked_obj, event def store_attachment(self, ij, pair, f_add): """ Stores the attached objects, and draws them.""" if f_add: self.attached_pairs.add((ij, pair)) self.show_attachment(ij, True) self.attach_pair(pair, True) else: try: self.attached_pairs.remove((ij, pair)) except KeyError: pass else: self.attach_pair(pair, False) self.show_attachment(ij, False) def clear_attachments(self): """ Clear all attachments.""" if self.marked: self.show_marked(self.marked, False) self.marked = None self.mark = None for ij, pair in self.attached_pairs: self.attach_pair(pair, False) self.show_attachment(ij, False) self.attached_pairs = set() # self.reset_compounds() self.contacts = None self.contact_bodies = None self.contact_points = None self.contact_bottoms = None def _make_mark(self, node, extent, name): """ Makes a mark GSO.""" mark = self.base_mark.copy() mat = node.getMat(self.scene) mark.apply_prop(dict(name=name), other=self.scene) mark.setMat(self.scene, mat) mark.setScale(self.scene, mark.getScale(self.scene) + extent) mark.wrtReparentTo(node) return mark def show_marked(self, node, f_on): """ Turns on/off marked graphic.""" if f_on: extent = Vec3(0.15, 0.15, 0.15) name = "mark" self.mark = self._make_mark(node, extent, name) self.mark.init_tree(tags=("model", )) # Exclude object from casting shadows self.mark.hide(self.shadow_mask) self.mark.setTransparency(TransparencyAttrib.MAlpha) self.mark.setDepthWrite(False) self.mark.setBin("fixed", 0, priority=5) else: self.mark.removeNode() def _make_connector(self, parent, points, extent, name): """ Makes connector object.""" connector = self.base_connector.copy() scale = Vec3(*(np.ptp(points, axis=0))) scale_extended = scale + extent pos = Point3(*(np.min(points, axis=0) + scale / 2.)) connector.apply_prop(dict(name=name, scale=scale_extended, pos=pos), other=self.scene) connector.wrtReparentTo(parent) return connector def show_attachment(self, ij, f_on): """ Turns on/off attachment graphic.""" if f_on: parent = self.contact_bottoms[ij] points = self.contact_points[ij] extent = Vec3(0.15, 0.15, 0.15) name = "connector_%d-%d" % ij self.connectors[ij] = self._make_connector(parent, points, extent, name) self.connectors[ij].init_tree(tags=("model", )) else: self.connectors.pop(ij).removeNode() # def attach_pair(self, pair, f_on): # """ Adds/removes physical attachment between a pair of nodes.""" # key = tuple(sorted(p.node() for p in pair)) # # key = frozenset(pair) # if f_on: # # Create the joint and add it. # self.joints[key] = self.joints.make_fixed(*pair) # else: # # Remove it. # del self.joints[key] def attach_physics(self): # Attach `self.scene` to the physics world. try: exclude = zip(*self.compound_components)[0] except IndexError: exclude = [] bnodes = [ bnode for bnode in self.scene.descendants(type_=PSO) if bnode not in exclude ] for bnode in bnodes: bnode.init_resources(tags=("shape", )) bnode.setCollideMask(BitMask32.allOn()) bnode.node().setDeactivationEnabled(False) self.bbase.attach(bnodes) def reset_compounds(self): for n, p in self.compound_components: n.wrtReparentTo(p) self.compound_components = [] for cnode in self.compound_objects: cnode.destroy_resources() cnode.removeNode() self.compound_objects = [] def make_attachment_graph(self): if not self.contact_bodies: return None n = len(self.contact_bodies) mtx = np.zeros((n, n), dtype="i") for (i, j), _ in self.attached_pairs: # i = self.contact_bodies.index(pair[0]) # j = self.contact_bodies.index(pair[1]) mtx[i, j] = 1 # mtx[j, i] = 1 graph = nx.from_numpy_matrix(mtx) return graph def attach_pair(self, pair, f_on): """ Adds/removes physical attachment between a pair of nodes.""" # Get the connected subgroups. graph = self.make_attachment_graph() sgs = [sg for sg in nx.connected_components(graph) if len(sg) > 1] self.reset_compounds() # Iterate over subgroups, creating compound shapes. for sg in sgs: nodes = [self.contact_bodies[i] for i in sg] parents = [c.getParent() for c in nodes] self.compound_components.extend(zip(nodes, parents)) cname = "+".join([str(i) for i in sorted(sg)]) cnode = CPSO(cname) cnode.reparentTo(self.scene) cnode.add(nodes) cnode.init_tree(tags=("shape", )) cnode.destroy_component_shapes() self.compound_objects.append(cnode)
class CameraControl(DirectObject): def __init__(self, panda3d): # Inicialización de variables self.winsize = [0, 0] self.panda3d = panda3d self.panda3d.mouse_on_workspace = False # Desabilita el comportamiento por defecto de la camara self.panda3d.disable_mouse() # Llama a la función self.window_rezise_event cuando la ventana cambia de tamaño self.accept('window-event', self.window_rezise_event) # self.panda3d.accept('aspectRatioChanged', lambda: print("ss")) # Creamos el punto donde se centrará la cámara target_pos = Point3(0., 0., 0.) self.panda3d.cam_target = self.panda3d.render.attach_new_node("camera_target") self.panda3d.cam_target.set_pos(target_pos) self.panda3d.camera.reparent_to(self.panda3d.cam_target) self.panda3d.camera.set_y(-50.) # Definimos la cambinación de teclas para el control de la camara self.camera_active = False self.orbit_mouse_btn = "mouse2" self.orbit_keyboard_btn = "shift" self.orbit_mouse_reference = None self.orbit_camera_reference = None self.pan_mouse_btn = "mouse2" self.pan_keyboard_btn = "mouse2" self.pan_mouse_reference = None self.pan_camera_reference = None self.zoom_mouse_btn = "mouse2" self.zoom_keyboard_btn = "control" self.zoom_mouse_reference = None self.zoom_camera_reference = None # Establecemos los valores máximos y minimos para el zoom self.max_zoom = 10 self.min_zoom = 0.1 # Creamos la tarea de control de la camara self.panda3d.task_mgr.add(self.camera_control_task, "camera_control") # El movimiento de la rueda del mouse controla el zoom self.panda3d.accept("wheel_up", self.zoom_in) self.panda3d.accept("wheel_down", self.zoom_out) # Una fución de prueba para comprobar la posición del mouse en el modelo 3d # self.panda3d.accept("mouse1", self.entity_select) # Se establece la lente ortografica en lugar de la perspectiva self.lens_type = "OrthographicLens" self.set_lens(self.lens_type) # Agrega un indicador de ejes en la esquina inferior izquierda self.corner = self.panda3d.camera.attachNewNode("corner of screen") # self.axis = self.panda3d.loader.loadModel("data/geom/custom-axis") # self.axis = self.panda3d.loader.loadModel("data/geom/view_gizmo_F") self.view_gizmo = list() self.view_gizmo.append(self.panda3d.loader.loadModel("data/geom/view_gizmo_compass")) # self.view_gizmo.append(self.panda3d.loader.loadModel("data/geom/view_gizmo_L")) # self.view_cube = ViewGizmoZone() # self.view_cube.set_geom(self.axis) for gizmo_geom in self.view_gizmo: gizmo_geom.setLightOff(1) # gizmo_geom.setColorScale(1,1,1,1) gizmo_geom.setShaderInput("colorborders", LVecBase4(0, 0, 0, 0.25)) gizmo = ViewGizmoZone() gizmo.set_geom(gizmo_geom) gizmo_geom.node().setBounds(BoundingSphere(Point3(0, 0, 0), 10)) gizmo_geom.node().setFinal(True) #gizmo_geom.showTightBounds() # gizmo_geom.showBounds() self.show_view_gizmo() # Agregamos una luz puntual en la ubicación de la camara plight = DirectionalLight("camera_light") plight.setColor((1, 1, 1, 1)) #plight.setAttenuation((1, 0, 0)) #print("getMaxDistance {}".format(plight.getMaxDistance())) self.panda3d.plight_node = self.panda3d.render.attach_new_node(plight) self.panda3d.plight_node.setPos(0, -50, 0) self.panda3d.render.setLight(self.panda3d.plight_node) self.panda3d.plight_node.reparentTo(self.panda3d.camera) # Agregamos luz ambiental que disminuya las zonas oscuras alight = AmbientLight('alight') alight.setColor((0.3, 0.3, 0.3, 1)) alnp = self.panda3d.render.attachNewNode(alight) self.panda3d.render.setLight(alnp) #def init_select_detection(self): self.traverser = CollisionTraverser("") # self.traverser.show_collisions(self.panda3d.render) self.picker_ray = CollisionRay() self.handler = CollisionHandlerQueue() self.picker_node = CollisionNode('mouseRay') self.picker_np = self.panda3d.camera.attachNewNode(self.picker_node) self.picker_node.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.picker_ray = CollisionRay() self.picker_node.addSolid(self.picker_ray) self.traverser.addCollider(self.picker_np, self.handler) def set_lens(self, lens_type="OrthographicLens"): """ Permite cambiar la lente de la camara :param lens_type: El tipo de lente a utilizar: OrthographicLens/PerspectiveLens :return: None """ self.lens_type = lens_type width = self.panda3d.win.getXSize() height = self.panda3d.win.getYSize() if lens_type is "OrthographicLens": lens = OrthographicLens() lens.setFilmSize(width, height ) if lens_type is "PerspectiveLens": lens = PerspectiveLens() lens.setFilmSize(width , height ) else: # Default value lens = OrthographicLens() lens.setFilmSize(width / 100, height / 100) print("new lens {}: {} {}".format(lens_type, width / 100, height / 100)) print(lens) self.panda3d.cam.node().setLens(lens) shader_control = self.panda3d.shader_control if shader_control is not None: shader_control.update_camera_lens(lens) def window_rezise_event(self, window=None): """ Se activa con cualquier evento de la ventana de windows, en caso de que haya cambiado de tamaño la ventana regenera la lente :param window: Información del evento :return: None """ if window is not None: # Window será igual a None si la aplicación panda3d no se inició wp = window.getProperties() newsize = [wp.getXSize(), wp.getYSize()] if self.winsize != newsize: self.winsize = newsize self.set_lens() self.show_view_gizmo() def mouse_is_over_workspace(self): """ Detecta si el mouse se encuentra dentro del area de trabajo del modelo 3d :return: True/False """ gui_objects = app.gui_objects is_over_workspace = False if self.panda3d.mouseWatcherNode.has_mouse() and app.workspace_active: is_over_workspace = True mouse_data = self.panda3d.win.getPointer(0) mouse_x, mouse_y = mouse_data.getX(), mouse_data.getY() for name, gui_obj in gui_objects.items(): if gui_obj.isHidden(): continue pos = gui_obj.getPos(pixel2d) frame_size = list(gui_obj["frameSize"]) x0 = pos[0] + frame_size[0] x1 = pos[0] + frame_size[1] y0 = -pos[2] - frame_size[2] y1 = -pos[2] - frame_size[3] x_left = min(x0, x1) x_right = max(x0, x1) y_top = min(y0, y1) y_bottom = max(y0, y1) #if name is "status_bar": #print(pos) #print("{} {} / {} {}".format(x_left, x_right, y_top, y_bottom)) overmouse_x = (x_left <= mouse_x <= x_right) overmouse_y = (y_top <= mouse_y <= y_bottom) # Revisa si el mouse se encuentra sobre un elemento de interfaz if overmouse_x and overmouse_y: # print("mouse is over {}".format(name)) is_over_workspace = False break app.mouse_on_workspace = is_over_workspace if is_over_workspace: get_mouse_3d_coords_task() return is_over_workspace def camera_control_task(self, task): """ Se ejecuta constantemente y realiza las tareas de movimiento de la camara según las teclas presionadas """ # El codigo se ejecuta si el mouse está dentro del espacio de trabajo o si ya se está realizando alguna acción if self.mouse_is_over_workspace() or self.camera_active: # Desactivamos el espacio de trabajo app.workspace_active = False # El nodo mouseWatcherNode permite recibir la entrada de mouse y teclado btn = self.panda3d.mouseWatcherNode # Obtenemos la posición del cursor mouse_data = self.panda3d.win.getPointer(0) mouse_pos = mouse_data.getX(), mouse_data.getY() # En función de la combinación de teclas se ejecuta una acción cam_task = 0 if btn.isButtonDown(self.orbit_mouse_btn) and btn.isButtonDown(self.orbit_keyboard_btn): cam_task = 1 elif btn.isButtonDown(self.zoom_mouse_btn) and btn.isButtonDown(self.zoom_keyboard_btn): cam_task = 2 elif btn.isButtonDown(self.pan_mouse_btn) and btn.isButtonDown(self.pan_keyboard_btn): cam_task = 3 # Orbit if cam_task is 1: self.camera_orbit(mouse_pos) self.camera_active = True else: self.orbit_mouse_reference = None # Zoom if cam_task is 2: self.camera_zoom(mouse_pos) self.camera_active = True else: self.zoom_mouse_reference = None # Pan if cam_task is 3: self.camera_pan(mouse_pos) self.camera_active = True else: self.pan_mouse_reference = None # Si la combinación de teclas no coincide con niguna acción se establece la camara como inactiva if cam_task is 0: self.camera_active = False # Se reactiva el espacio de trabajo app.workspace_active = True self.entity_select() else: pass # Actualizamos la posición de la luz puntual #cam = self.panda3d.camera #self.panda3d.plight_node.setPos(cam.get_pos(self.panda3d.render)) # Ejecutar solo en windows """if os.name == 'nt': # Se coloca la camra en determinadas vistas (frontal, lateral, superior, etc) al apretar el # teclado numérico # Lista de teclas http://www.kbdedit.com/manual/low_level_vk_list.html target = self.panda3d.cam_target if win32api.GetAsyncKeyState(win32con.VK_NUMPAD1): target.set_hpr(0, 0, 0.) elif win32api.GetAsyncKeyState(win32con.VK_NUMPAD3): target.set_hpr(90, 0, 0.) elif win32api.GetAsyncKeyState(win32con.VK_NUMPAD7): target.set_hpr(0, -90, 0.)""" return task.cont def camera_orbit(self, mouse_pos): """ Orbita la camara alrededor del objetivo de ésta, según la posición del mouse respecto del punto donde se hizo click """ target = self.panda3d.cam_target if self.orbit_mouse_reference is None: self.orbit_mouse_reference = mouse_pos self.orbit_camera_reference = target.get_hpr() x_diff = self.orbit_mouse_reference[0] - mouse_pos[0] y_diff = self.orbit_mouse_reference[1] - mouse_pos[1] new_h = self.orbit_camera_reference[0] + x_diff / 4 new_p = self.orbit_camera_reference[1] + y_diff / 4 target.set_hpr(new_h, new_p, 0.) def camera_pan(self, mouse_pos): """ Panea la camara alrededor del objetivo de ésta, según la posición del mouse respecto del punto donde se hizo click """ target = self.panda3d.camera if self.pan_mouse_reference is None: self.pan_mouse_reference = mouse_pos self.pan_camera_reference = target.get_pos() x_diff = self.pan_mouse_reference[0] - mouse_pos[0] y_diff = self.pan_mouse_reference[1] - mouse_pos[1] new_x = self.pan_camera_reference[0] + x_diff / 100 new_y = self.pan_camera_reference[1] new_z = self.pan_camera_reference[2] - y_diff / 100 target.set_pos(new_x, new_y, new_z) def camera_zoom(self, mouse_pos): """ Orbita la camara alrededor del objetivo de ésta, según la posición del mouse respecto del punto donde se hizo click """ target = self.panda3d.cam_target if self.zoom_mouse_reference is None: self.zoom_mouse_reference = mouse_pos self.zoom_camera_reference = target.getScale()[0] y_diff = self.zoom_mouse_reference[1] - mouse_pos[1] new_scale = self.zoom_camera_reference * math.exp(y_diff/100) new_scale = max(new_scale, 0.1) new_scale = min(new_scale, 10) target.setScale(new_scale, new_scale, new_scale) def zoom_in(self): if self.mouse_is_over_workspace(): target = self.panda3d.cam_target old_scale = target.getScale()[0] new_scale = old_scale - 0.1 * old_scale new_scale = max(new_scale, self.min_zoom) target.setScale(new_scale, new_scale, new_scale) def zoom_out(self): if self.mouse_is_over_workspace(): target = self.panda3d.cam_target old_scale = target.getScale()[0] new_scale = old_scale + 0.1 * old_scale new_scale = min(new_scale, self.max_zoom) target.setScale(new_scale, new_scale, new_scale) def show_view_gizmo(self): """ Agrega un indicador de ejes en la esquina inferior izquierda """ scale = 0.075 width = self.panda3d.win.getXSize()/100 height = self.panda3d.win.getYSize()/100 #self.corner.setPos(width / 2 - 10 * scale, 5, height / 2 - 28 * scale) self.corner.setPos(width / 2-1, 5, height / 2 - 2.4) print("DEBUG SHOW VIEW CUBE") print(height) print(height / 2 - 28 * scale) # Dibujar por encima de todos los objetos for gizmo_geom in self.view_gizmo: gizmo_geom.setLightOff(1) # gizmo_geom.setBin("fixed", 0) # gizmo_geom.set_two_sided(True) """ Tarea pendiente: Hay que corregir un error por el cual el indicador de ejes no se dubuja por encima de todos los objetos pudiendo intersectarse cona las geometrías del modelo Simplemente es un error visual, no afecta al funcionamiento axis.setDepthTest(False) https://discourse.panda3d.org/t/model-always-on-screen/8135/5 """ gizmo_geom.setScale(scale) # axis.setScale(1) gizmo_geom.reparentTo(self.corner) # gizmo_geom.setPos(0, 0, 0) gizmo_geom.setCompass() separation = 1 # gizmo_geom.setShaderInput("showborders", LVecBase4(0)) # gizmo_geom.setShaderInput("colorborders", LVecBase4(0, 0, 0, 0)) # gizmo_geom.setShaderInput("separation", LVecBase4(separation, 0, separation, 0)) def add_cube(self): """ Función de prueba, coloca cubos en la ubicación del cursor """ if self.panda3d.mouse_on_workspace: print("add_cube") pos = self.panda3d.work_plane_mouse cube = self.panda3d.loader.loadModel("models/box") # Reparent the model to render. cube.reparentTo(self.panda3d.render) # Apply scale and position transforms on the model. cube.setScale(0.25, 0.25, 0.25) cube.setPos(pos[0], pos[1], pos[2]) def entity_select(self): if self.panda3d.mouseWatcherNode.hasMouse(): """traverser = CollisionTraverser("") #traverser.show_collisions(render) picker_ray = CollisionRay() handler = CollisionHandlerQueue() picker_node = CollisionNode('mouseRay') picker_np = self.panda3d.camera.attachNewNode(picker_node) picker_node.setFromCollideMask(GeomNode.getDefaultCollideMask()) picker_ray = CollisionRay() picker_node.addSolid(picker_ray) traverser.addCollider(picker_np, handler) picker_node.setFromCollideMask(GeomNode.getDefaultCollideMask()) mpos = self.panda3d.mouseWatcherNode.getMouse() picker_ray.setFromLens(self.panda3d.camNode, mpos.getX(), mpos.getY()) traverser.traverse(self.panda3d.render)""" self.picker_node.setFromCollideMask(GeomNode.getDefaultCollideMask()) mpos = self.panda3d.mouseWatcherNode.getMouse() self.picker_ray.setFromLens(self.panda3d.camNode, mpos.getX(), mpos.getY()) self.traverser.traverse(self.panda3d.render) handler = self.handler # Assume for simplicity's sake that myHandler is a CollisionHandlerQueue. btn = self.panda3d.mouseWatcherNode if handler.getNumEntries() > 0: # This is so we get the closest object. handler.sortEntries() entity = handler.getEntry(0).getIntoNodePath() entity = entity.findNetTag('entity_id') if not entity.isEmpty(): #print("entity selected: {}".format(entity.getTag("entity_id"))) entity_id = entity.getTag("entity_id") entity_type = entity.getTag("entity_type") #print(entity_type) model = app.model_reg category_type = model.get(entity_type, dict()) entity = category_type.get(entity_id, None) #print(entity) if btn.isButtonDown("mouse1"): entity.on_click() if entity.is_editable: prop_editor = app.main_ui.prop_editor prop_editor.entity_read(entity) elif entity.is_selectable: status_bar = app.main_ui.status_bar status_bar.entity_read(entity) else: print("Hay {} entidades bajo el mouse".format(handler.getNumEntries())) else: if btn.isButtonDown("mouse1"): entities = app.model_reg.get("View", {}) if entities is None or len(entities) is 0: View() entities = app.model_reg.get("View") entity = list(entities.values())[0] prop_editor = app.main_ui.prop_editor prop_editor.entity_read(entity) else: status_bar = app.main_ui.status_bar status_bar.entity_read()
class p3dApp(ShowBase): def __init__(self): ShowBase.__init__(self); # setup the environment or model self.model = \ self.loader.loadModel("/Developer/Panda3D/models/box.egg.pz"); self.model.reparentTo(self.render); self.model.setTag('Model', '1'); self.model.setScale(1.5, 1.5, 1.5); # setup camera self.camera.setPos(5,5,5) self.camera.lookAt(0,0,0) # Disable mouse control self.disableMouse(); # Handle mouse events. self.accept('mouse1', self.mouse_down); # convert image from opencv to panda3d texture # self.taskMgr.add(self.read_image_cv, "cvImageTask"); # Setup collision handler self.handler = CollisionHandlerQueue() self.traverser = CollisionTraverser('ColTraverser') self.traverser.traverse(self.model) self.ray = CollisionRay() pickerNode = CollisionNode('MouseRay') pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) pickerNP = self.camera.attachNewNode(pickerNode) pickerNode.addSolid(self.ray) self.traverser.addCollider(pickerNP, self.handler) self.load_shader(); self.first_frame_loaded = False; def read_image_cv(self): """ Pulls the next frame from the opencv part, and converts to a panda3d texture and display it on the screen. """ cvim = self.cvapp.pull_frame() w = cvim.shape[1]; h = cvim.shape[0]; cvim = cv2.flip(cvim, 0); self.im = Texture("cvIm"); self.im.setCompression(Texture.CMOff); self.im.setup2dTexture(w, h, Texture.TUnsignedByte, Texture.FLuminance); self.im.setRamImage(cvim); self.screen_im = OnscreenImage(parent=self.render2d, image=self.im, scale=(1, 1, 1), pos=(0, 0, 0)); self.cam2d.node().getDisplayRegion(0).setSort(-20); def load_shader(self): """ The function loads the vertex and fragment shader. It provides an example of sending the model-view-projection matrix to the shader program when it's calculated. """ self.shader = Shader.load(Shader.SL_GLSL, "vertex.glsl", "fragment.glsl"); self.model.set_shader(self.shader) self.model.set_shader_input("my_ModelViewProjectionMatrix", LMatrix4f()) def mouse_down(self): """ This function is called as a result of a mouse click. It gets the vertex that was clicked by the mouse. It sends the mouse position and the vertex position to the cv app. """ if (self.first_frame_loaded == False): self.first_frame_loaded = True self.read_image_cv() return; xPos = self.mouseWatcherNode.getMouseX() yPos = self.mouseWatcherNode.getMouseY() self.ray.setFromLens(self.camNode, xPos, yPos) self.traverser.traverse(self.model) self.handler.sortEntries() if (self.handler.getNumEntries() > 0): entry = self.handler.getEntry(0) # CollisionEntry vpos = entry.getSurfacePoint(self.model) res = self.cvapp.mouse_clicked(LPoint3f(xPos, yPos), vpos) if (res == 1): self.read_image_cv() def set_cv_app(self, cvapp): self.cvapp = cvapp;
class World(DirectObject): def __init__(self): #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking", style=1, fg=(1, 1, 1, 1), pos=(0.8, -0.95), scale=.07) self.escapeEvent = OnscreenText(text="ESC: Quit", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale=.05) self.mouse1Event = OnscreenText( text="Left-click and drag: Pick up and drag piece", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.90), align=TextNode.ALeft, scale=.05) self.accept('escape', sys.exit) #Escape quits base.disableMouse() #Disble mouse camera control camera.setPosHpr(0, -13.75, 6, 0, -25, 0) #Set the camera self.setupLights() #Setup default lighting #Since we are using collision detection to do picking, we set it up like #any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() #Make a traverser self.pq = CollisionHandlerQueue() #Make a handler #Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') #Attach that node to the camera since the ray will need to be positioned #relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() #Make our ray self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node #Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) #Now we create the chess board and its pieces #We will attach all of the squares to their own root. This way we can do the #collision pass just on the sqaures and save the time of checking the rest #of the scene self.squareRoot = render.attachNewNode("squareRoot") #For each square self.squares = [None for i in range(64)] self.pieces = [None for i in range(64)] for i in range(64): #Load, parent, color, and position the model (a single square polygon) self.squares[i] = loader.loadModel("models/square") self.squares[i].reparentTo(self.squareRoot) self.squares[i].setPos(SquarePos(i)) self.squares[i].setColor(SquareColor(i)) #Set the model itself to be collideable with the ray. If this model was #any more complex than a single polygon, you should set up a collision #sphere around it instead. But for single polygons this works fine. self.squares[i].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) #Set a tag on the square's node so we can look up what square this is #later during the collision pass self.squares[i].find("**/polygon").node().setTag('square', str(i)) #We will use this variable as a pointer to whatever piece is currently #in this square #The order of pieces on a chessboard from white's perspective. This list #contains the constructor functions for the piece classes defined below pieceOrder = (Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook) for i in range(8, 16): #Load the white pawns self.pieces[i] = Pawn(i, WHITE) for i in range(48, 56): #load the black pawns self.pieces[i] = Pawn(i, PIECEBLACK) for i in range(8): #Load the special pieces for the front row and color them white self.pieces[i] = pieceOrder[i](i, WHITE) #Load the special pieces for the back row and color them black self.pieces[i + 56] = pieceOrder[i](i + 56, PIECEBLACK) #This will represent the index of the currently highlited square self.hiSq = False #This wil represent the index of the square where currently dragged piece #was grabbed from self.dragging = False #Start the task that handles the picking self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') self.accept("mouse1", self.grabPiece) #left-click grabs a piece self.accept("mouse1-up", self.releasePiece) #releasing places it #This function swaps the positions of two pieces def swapPieces(self, fr, to): temp = self.pieces[fr] self.pieces[fr] = self.pieces[to] self.pieces[to] = temp if self.pieces[fr]: self.pieces[fr].square = fr self.pieces[fr].obj.setPos(SquarePos(fr)) if self.pieces[to]: self.pieces[to].square = to self.pieces[to].obj.setPos(SquarePos(to)) def mouseTask(self, task): #This task deals with the highlighting and dragging based on the mouse #First, clear the current highlight if self.hiSq is not False: self.squares[self.hiSq].setColor(SquareColor(self.hiSq)) self.hiSq = False #Check to see if we can access the mouse. We need it to do anything else if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #If we are dragging something, set the position of the object #to be at the appropriate point over the plane of the board if self.dragging is not False: #Gets the point described by pickerRay.getOrigin(), which is relative to #camera, relative instead to render nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) #Same thing with the direction of the ray nearVec = render.getRelativeVector( camera, self.pickerRay.getDirection()) self.pieces[self.dragging].obj.setPos( PointAtZ(.5, nearPoint, nearVec)) #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.squareRoot) if self.pq.getNumEntries() > 0: #if we have hit something, sort the hits so that the closest #is first, and highlight that node self.pq.sortEntries() i = int(self.pq.getEntry(0).getIntoNode().getTag('square')) #Set the highlight on the picked square self.squares[i].setColor(HIGHLIGHT) self.hiSq = i return Task.cont def grabPiece(self): #If a square is highlighted and it has a piece, set it to dragging mode if (self.hiSq is not False and self.pieces[self.hiSq]): self.dragging = self.hiSq self.hiSq = False def releasePiece(self): #Letting go of a piece. If we are not on a square, return it to its original #position. Otherwise, swap it with the piece in the new square if self.dragging is not False: #Make sure we really are dragging something #We have let go of the piece, but we are not on a square if self.hiSq is False: self.pieces[self.dragging].obj.setPos(SquarePos(self.dragging)) else: #Otherwise, swap the pieces self.swapPieces(self.dragging, self.hiSq) #We are no longer dragging anything self.dragging = False def setupLights(self): #This function sets up some default lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(.8, .8, .8, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(0, 45, -45)) directionalLight.setColor(Vec4(0.2, 0.2, 0.2, 1)) render.setLight(render.attachNewNode(directionalLight)) render.setLight(render.attachNewNode(ambientLight))
class LocationSeeker: def __init__(self, avatar, minDistance, maxDistance, shadowScale = 1): self.dropShadowPath = 'phase_3/models/props/square_drop_shadow.bam' self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.mp3' self.moveShadowTaskName = 'Move Shadow' self.locationSelectedName = 'Location Selected' self.dropShadow = None self.shadowScale = shadowScale self.rejectSfx = loader.loadSfx(self.rejectSoundPath) self.avatar = avatar self.cameraNode = None self.cameraRay = None self.cameraNP = None self.shadowNP = None self.shadowRay = None self.minDistance = minDistance self.maxDistance = maxDistance self.legacyMode = False self.collHdlFl = CollisionHandlerQueue() return def startSeeking(self): if not self.avatar: return self.cleanupShadow() self.buildShadow() scale = self.dropShadow.getScale() if scale < 1.0: self.maxDistance += 40 x, y, z = self.avatar.getPos(render) self.dropShadow.reparentTo(render) self.dropShadow.setPos(x, y + 5, z + 2) self.cameraNode = CollisionNode('coll_camera') self.cameraNode.setFromCollideMask(CIGlobals.WallBitmask) self.cameraRay = CollisionRay() self.cameraNode.addSolid(self.cameraRay) self.cameraNP = camera.attachNewNode(self.cameraNode) base.cTrav.addCollider(self.cameraNP, CollisionHandlerQueue()) if not self.legacyMode: shadowNode = CollisionNode('coll_shadow') self.shadowRay = CollisionRay(0, 0, 6, 0, 0, -1) shadowNode.addSolid(self.shadowRay) shadowNode.setFromCollideMask(CIGlobals.FloorBitmask) self.shadowNP = self.dropShadow.attachNewNode(shadowNode) base.cTrav.addCollider(self.shadowNP, self.collHdlFl) base.taskMgr.add(self.__moveShadow, self.moveShadowTaskName) self.avatar.acceptOnce('mouse1', self.locationChosen) def stopSeeking(self): base.taskMgr.remove(self.moveShadowTaskName) def __moveShadow(self, task): if base.mouseWatcherNode.hasMouse(): def PointAtZ(z, point, vec): if vec.getZ() != 0: return point + vec * ((z - point.getZ()) / vec.getZ()) else: return self.getLocation() mouse = base.mouseWatcherNode.getMouse() self.cameraRay.setFromLens(base.camNode, mouse.getX(), mouse.getY()) nearPoint = render.getRelativePoint(camera, self.cameraRay.getOrigin()) nearVec = render.getRelativeVector(camera, self.cameraRay.getDirection()) self.dropShadow.setPos(PointAtZ(0.5, nearPoint, nearVec)) if self.legacyMode: self.dropShadow.setZ(base.localAvatar.getZ(render) + 0.5) elif self.collHdlFl.getNumEntries() > 0: self.dropShadow.setZ(self.collHdlFl.getEntry(0).getSurfacePoint(render).getZ() + 0.5) return Task.cont def locationChosen(self): base.taskMgr.remove(self.moveShadowTaskName) distance = self.avatar.getDistance(self.dropShadow) x, y, z = self.getLocation() if distance >= self.minDistance and distance <= self.maxDistance: gag = self.avatar.getBackpack().getActiveGag() self.avatar.sendUpdate('setDropLoc', [gag.getID(), x, y, z]) messenger.send(self.locationSelectedName) else: self.rejectSfx.play() self.avatar.acceptOnce('mouse1', self.locationChosen) base.taskMgr.add(self.__moveShadow, self.moveShadowTaskName) def buildShadow(self): self.cleanupShadow() if not self.dropShadowPath or not self.avatar: return self.dropShadow = loader.loadModel(self.dropShadowPath) self.dropShadow.setScale(self.shadowScale) def setShadowType(self, isCircle = False, scale = 1): if not isCircle: self.dropShadowPath = 'phase_3/models/props/square_drop_shadow.bam' else: self.dropShadowPath = 'phase_3/models/props/drop_shadow.bam' self.shadowScale = scale def getLocation(self): if self.dropShadow: return self.dropShadow.getPos(render) return self.avatar.getPos(render) def getLocationSelectedName(self): return self.locationSelectedName def cleanupShadow(self): if self.dropShadow: self.dropShadow.removeNode() self.dropShadow = None if self.cameraNode: self.cameraNP.removeNode() self.cameraNP = None self.cameraNode = None self.cameraRay = None self.shadowNP.removeNode() self.shadowRay = None self.shadowNP = None self.shadowSphNP = None return def cleanup(self): if self.avatar: base.taskMgr.remove(self.moveShadowTaskName) self.avatar.ignore('mouse1') self.cleanupShadow() self.rejectSfx.stop() self.rejectSfx = None self.avatar = None self.dropShadowPath = None self.rejectSoundPath = None self.locationSelectedName = None self.moveShadowTaskName = None del self.shadowScale return
class Viewport(QtWidgets.QWidget, DirectObject): ClearColor = LEGlobals.vec3GammaToLinear(Vec4(0.361, 0.361, 0.361, 1.0)) def __init__(self, vpType, window, doc): DirectObject.__init__(self) QtWidgets.QWidget.__init__(self, window) self.doc = doc self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setMouseTracking(True) self.qtWindow = None self.qtWidget = None self.window = window self.type = vpType self.spec = VIEWPORT_SPECS[self.type] self.lens = None self.camNode = None self.camera = None self.cam = None self.win = None self.displayRegion = None self.mouseWatcher = None self.mouseWatcherNp = None self.buttonThrower = None self.clickRay = None self.clickNode = None self.clickNp = None self.clickQueue = None self.tickTask = None self.zoom = 1.0 self.gizmo = None self.inputDevice = None self.mouseAndKeyboard = None self.lastRenderTime = 0.0 self.enabled = False self.needsUpdate = True # 2D stuff copied from ShowBase :( self.camera2d = None self.cam2d = None self.render2d = None self.aspect2d = None self.a2dBackground = None self.a2dTop = None self.a2dBottom = None self.a2dLeft = None self.a2dRight = None self.a2dTopCenter = None self.a2dTopCenterNs = None self.a2dBottomCenter = None self.a2dBottomCenterNs = None self.a2dRightCenter = None self.a2dRightCenterNs = None self.a2dTopLeft = None self.a2dTopLeftNs = None self.a2dTopRight = None self.a2dTopRightNs = None self.a2dBottomLeft = None self.a2dBottomLeftNs = None self.a2dBottomRight = None self.a2dBottomRightNs = None self.__oldAspectRatio = None self.gridRoot = self.doc.render.attachNewNode("gridRoot") self.gridRoot.setLightOff(1) #self.gridRoot.setBSPMaterial("phase_14/materials/unlit.mat") #self.gridRoot.setDepthWrite(False) self.gridRoot.setBin("background", 0) self.gridRoot.hide(~self.getViewportMask()) self.grid = None def updateView(self, now=False): if now: self.renderView() else: self.needsUpdate = True def getGizmoAxes(self): raise NotImplementedError def getMouseRay(self, collRay=False): ray = CollisionRay() ray.setFromLens(self.camNode, self.getMouse()) if collRay: return ray else: return Ray(ray.getOrigin(), ray.getDirection()) def hasMouse(self): return self.mouseWatcher.hasMouse() def getMouse(self): if self.mouseWatcher.hasMouse(): return self.mouseWatcher.getMouse() return Point2(0, 0) def is3D(self): return self.type == VIEWPORT_3D def is2D(self): return self.type != VIEWPORT_3D def makeGrid(self): raise NotImplementedError def getViewportMask(self): return BitMask32.bit(self.type) def getViewportFullMask(self): return self.getViewportMask() def makeLens(self): raise NotImplementedError def getGridAxes(self): raise NotImplementedError def expand(self, point): return point def initialize(self): self.lens = self.makeLens() self.camera = self.doc.render.attachNewNode( ModelNode("viewportCameraParent")) self.camNode = Camera("viewportCamera") self.camNode.setLens(self.lens) self.camNode.setCameraMask(self.getViewportMask()) self.cam = self.camera.attachNewNode(self.camNode) winprops = WindowProperties.getDefault() winprops.setParentWindow(int(self.winId())) winprops.setForeground(False) winprops.setUndecorated(True) gsg = self.doc.gsg output = base.graphicsEngine.makeOutput( base.pipe, "viewportOutput", 0, FrameBufferProperties.getDefault(), winprops, (GraphicsPipe.BFFbPropsOptional | GraphicsPipe.BFRequireWindow), gsg) self.qtWindow = QtGui.QWindow.fromWinId( output.getWindowHandle().getIntHandle()) self.qtWidget = QtWidgets.QWidget.createWindowContainer( self.qtWindow, self, QtCore.Qt.WindowDoesNotAcceptFocus | QtCore.Qt.WindowTransparentForInput | QtCore.Qt.WindowStaysOnBottomHint | QtCore.Qt.BypassWindowManagerHint | QtCore.Qt.SubWindow) #, #(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowDoesNotAcceptFocus #| QtCore.Qt.WindowTransparentForInput | QtCore.Qt.BypassWindowManagerHint #| QtCore.Qt.SubWindow | QtCore.Qt.WindowStaysOnBottomHint)) self.qtWidget.setFocusPolicy(QtCore.Qt.NoFocus) self.inputDevice = output.getInputDevice(0) assert output is not None, "Unable to create viewport output!" dr = output.makeDisplayRegion() dr.disableClears() dr.setCamera(self.cam) self.displayRegion = dr output.disableClears() output.setClearColor(Viewport.ClearColor) output.setClearColorActive(True) output.setClearDepthActive(True) output.setActive(True) self.win = output # keep track of the mouse in this viewport mak = MouseAndKeyboard(self.win, 0, "mouse") mouse = base.dataRoot.attachNewNode(mak) self.mouseAndKeyboard = mouse self.mouseWatcher = MouseWatcher() self.mouseWatcher.setDisplayRegion(self.displayRegion) mw = mouse.attachNewNode(self.mouseWatcher) self.mouseWatcherNp = mw # listen for keyboard and mouse events in this viewport bt = ButtonThrower("kbEvents") bt.setButtonDownEvent("btndown") bt.setButtonUpEvent("btnup") mods = ModifierButtons() mods.addButton(KeyboardButton.shift()) mods.addButton(KeyboardButton.control()) mods.addButton(KeyboardButton.alt()) mods.addButton(KeyboardButton.meta()) bt.setModifierButtons(mods) self.buttonThrower = mouse.attachNewNode(bt) # collision objects for clicking on objects from this viewport self.clickRay = CollisionRay() self.clickNode = CollisionNode("viewportClickRay") self.clickNode.addSolid(self.clickRay) self.clickNp = NodePath(self.clickNode) self.clickQueue = CollisionHandlerQueue() self.setupRender2d() self.setupCamera2d() self.gizmo = ViewportGizmo(self) self.doc.viewportMgr.addViewport(self) self.makeGrid() def cleanup(self): self.grid.cleanup() self.grid = None self.gridRoot.removeNode() self.gridRoot = None self.lens = None self.camNode = None self.cam.removeNode() self.cam = None self.camera.removeNode() self.camera = None self.spec = None self.doc = None self.type = None self.window = None self.zoom = None self.gizmo.cleanup() self.gizmo = None self.clickNp.removeNode() self.clickNp = None self.clickQueue.clearEntries() self.clickQueue = None self.clickNode = None self.clickRay = None self.buttonThrower.removeNode() self.buttonThrower = None self.inputDevice = None self.mouseWatcherNp.removeNode() self.mouseWatcherNp = None self.mouseWatcher = None self.mouseAndKeyboard.removeNode() self.mouseAndKeyboard = None self.win.removeAllDisplayRegions() self.displayRegion = None base.graphicsEngine.removeWindow(self.win) self.win = None self.camera2d.removeNode() self.camera2d = None self.cam2d = None self.render2d.removeNode() self.render2d = None self.a2dBackground = None self.a2dTop = None self.a2dBottom = None self.a2dLeft = None self.a2dRight = None self.aspect2d = None self.a2dTopCenter = None self.a2dTopCenterNs = None self.a2dBottomCenter = None self.a2dBottomCenterNs = None self.a2dLeftCenter = None self.a2dLeftCenterNs = None self.a2dRightCenter = None self.a2dRightCenterNs = None self.a2dTopLeft = None self.a2dTopLeftNs = None self.a2dTopRight = None self.a2dTopRightNs = None self.a2dBottomLeft = None self.a2dBottomLeftNs = None self.a2dBottomRight = None self.a2dBottomRightNs = None self.__oldAspectRatio = None self.qtWindow.deleteLater() self.qtWidget.deleteLater() self.qtWindow = None self.qtWidget = None self.deleteLater() def keyPressEvent(self, event): button = LEUtils.keyboardButtonFromQtKey(event.key()) if button: self.inputDevice.buttonDown(button) def keyReleaseEvent(self, event): button = LEUtils.keyboardButtonFromQtKey(event.key()) if button: self.inputDevice.buttonUp(button) def enterEvent(self, event): # Give ourselves focus. self.setFocus() QtWidgets.QWidget.enterEvent(self, event) def mouseMoveEvent(self, event): self.inputDevice.setPointerInWindow(event.pos().x(), event.pos().y()) QtWidgets.QWidget.mouseMoveEvent(self, event) def leaveEvent(self, event): self.clearFocus() self.inputDevice.setPointerOutOfWindow() self.inputDevice.focusLost() QtWidgets.QWidget.leaveEvent(self, event) def mousePressEvent(self, event): btn = event.button() if btn == QtCore.Qt.LeftButton: self.inputDevice.buttonDown(MouseButton.one()) elif btn == QtCore.Qt.MiddleButton: self.inputDevice.buttonDown(MouseButton.two()) elif btn == QtCore.Qt.RightButton: self.inputDevice.buttonDown(MouseButton.three()) QtWidgets.QWidget.mousePressEvent(self, event) def mouseReleaseEvent(self, event): btn = event.button() if btn == QtCore.Qt.LeftButton: self.inputDevice.buttonUp(MouseButton.one()) elif btn == QtCore.Qt.MiddleButton: self.inputDevice.buttonUp(MouseButton.two()) elif btn == QtCore.Qt.RightButton: self.inputDevice.buttonUp(MouseButton.three()) QtWidgets.QWidget.mouseReleaseEvent(self, event) def wheelEvent(self, event): ang = event.angleDelta().y() if ang > 0: self.inputDevice.buttonDown(MouseButton.wheelUp()) self.inputDevice.buttonUp(MouseButton.wheelUp()) else: self.inputDevice.buttonDown(MouseButton.wheelDown()) self.inputDevice.buttonUp(MouseButton.wheelDown()) QtWidgets.QWidget.wheelEvent(self, event) def getAspectRatio(self): return self.win.getXSize() / self.win.getYSize() def setupRender2d(self): ## This is the root of the 2-D scene graph. self.render2d = NodePath("viewport-render2d") # Set up some overrides to turn off certain properties which # we probably won't need for 2-d objects. # It's probably important to turn off the depth test, since # many 2-d objects will be drawn over each other without # regard to depth position. # We used to avoid clearing the depth buffer before drawing # render2d, but nowadays we clear it anyway, since we # occasionally want to put 3-d geometry under render2d, and # it's simplest (and seems to be easier on graphics drivers) # if the 2-d scene has been cleared first. self.render2d.setDepthTest(0) self.render2d.setDepthWrite(0) self.render2d.setMaterialOff(1) self.render2d.setTwoSided(1) self.aspect2d = self.render2d.attachNewNode("viewport-aspect2d") aspectRatio = self.getAspectRatio() self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0) self.a2dBackground = self.aspect2d.attachNewNode("a2dBackground") ## The Z position of the top border of the aspect2d screen. self.a2dTop = 1.0 ## The Z position of the bottom border of the aspect2d screen. self.a2dBottom = -1.0 ## The X position of the left border of the aspect2d screen. self.a2dLeft = -aspectRatio ## The X position of the right border of the aspect2d screen. self.a2dRight = aspectRatio self.a2dTopCenter = self.aspect2d.attachNewNode("a2dTopCenter") self.a2dTopCenterNs = self.aspect2d.attachNewNode("a2dTopCenterNS") self.a2dBottomCenter = self.aspect2d.attachNewNode("a2dBottomCenter") self.a2dBottomCenterNs = self.aspect2d.attachNewNode( "a2dBottomCenterNS") self.a2dLeftCenter = self.aspect2d.attachNewNode("a2dLeftCenter") self.a2dLeftCenterNs = self.aspect2d.attachNewNode("a2dLeftCenterNS") self.a2dRightCenter = self.aspect2d.attachNewNode("a2dRightCenter") self.a2dRightCenterNs = self.aspect2d.attachNewNode("a2dRightCenterNS") self.a2dTopLeft = self.aspect2d.attachNewNode("a2dTopLeft") self.a2dTopLeftNs = self.aspect2d.attachNewNode("a2dTopLeftNS") self.a2dTopRight = self.aspect2d.attachNewNode("a2dTopRight") self.a2dTopRightNs = self.aspect2d.attachNewNode("a2dTopRightNS") self.a2dBottomLeft = self.aspect2d.attachNewNode("a2dBottomLeft") self.a2dBottomLeftNs = self.aspect2d.attachNewNode("a2dBottomLeftNS") self.a2dBottomRight = self.aspect2d.attachNewNode("a2dBottomRight") self.a2dBottomRightNs = self.aspect2d.attachNewNode("a2dBottomRightNS") # Put the nodes in their places self.a2dTopCenter.setPos(0, 0, self.a2dTop) self.a2dTopCenterNs.setPos(0, 0, self.a2dTop) self.a2dBottomCenter.setPos(0, 0, self.a2dBottom) self.a2dBottomCenterNs.setPos(0, 0, self.a2dBottom) self.a2dLeftCenter.setPos(self.a2dLeft, 0, 0) self.a2dLeftCenterNs.setPos(self.a2dLeft, 0, 0) self.a2dRightCenter.setPos(self.a2dRight, 0, 0) self.a2dRightCenterNs.setPos(self.a2dRight, 0, 0) self.a2dTopLeft.setPos(self.a2dLeft, 0, self.a2dTop) self.a2dTopLeftNs.setPos(self.a2dLeft, 0, self.a2dTop) self.a2dTopRight.setPos(self.a2dRight, 0, self.a2dTop) self.a2dTopRightNs.setPos(self.a2dRight, 0, self.a2dTop) self.a2dBottomLeft.setPos(self.a2dLeft, 0, self.a2dBottom) self.a2dBottomLeftNs.setPos(self.a2dLeft, 0, self.a2dBottom) self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom) self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom) def setupCamera2d(self, sort=10, displayRegion=(0, 1, 0, 1), coords=(-1, 1, -1, 1)): dr = self.win.makeMonoDisplayRegion(*displayRegion) dr.setSort(10) # Enable clearing of the depth buffer on this new display # region (see the comment in setupRender2d, above). dr.setClearDepthActive(1) # Make any texture reloads on the gui come up immediately. dr.setIncompleteRender(False) left, right, bottom, top = coords # Now make a new Camera node. cam2dNode = Camera('cam2d') lens = OrthographicLens() lens.setFilmSize(right - left, top - bottom) lens.setFilmOffset((right + left) * 0.5, (top + bottom) * 0.5) lens.setNearFar(-1000, 1000) cam2dNode.setLens(lens) # self.camera2d is the analog of self.camera, although it's # not as clear how useful it is. self.camera2d = self.render2d.attachNewNode('camera2d') camera2d = self.camera2d.attachNewNode(cam2dNode) dr.setCamera(camera2d) self.cam2d = camera2d return camera2d def mouse1Up(self): pass def mouse1Down(self): pass def mouse2Up(self): pass def mouse2Down(self): pass def mouse3Up(self): pass def mouse3Down(self): pass def mouseEnter(self): self.updateView() def mouseExit(self): pass def mouseMove(self): pass def wheelUp(self): pass def wheelDown(self): pass def shouldRender(self): if not self.enabled: return False now = globalClock.getRealTime() if self.lastRenderTime != 0: elapsed = now - self.lastRenderTime if elapsed <= 0: return False frameRate = 1 / elapsed if frameRate > 100.0: # Never render faster than 100Hz return False return self.needsUpdate def renderView(self): self.lastRenderTime = globalClock.getRealTime() self.needsUpdate = False #self.win.setActive(1) base.requestRender() def tick(self): if self.shouldRender(): self.renderView() else: pass #self.win.setActive(0) def getViewportName(self): return self.spec.name def getViewportCenterPixels(self): return LPoint2i(self.win.getXSize() // 2, self.win.getYSize() // 2) def centerCursor(self, cursor): center = self.getViewportCenterPixels() cursor.setPos( self.mapToGlobal(QtCore.QPoint(self.width() / 2, self.height() / 2))) self.inputDevice.setPointerInWindow(center.x, center.y) def viewportToWorld(self, viewport, vec=False): front = Point3() back = Point3() self.lens.extrude(viewport, front, back) world = (front + back) / 2 worldMat = self.cam.getMat(render) if vec: world = worldMat.xformVec(world) else: world = worldMat.xformPoint(world) return world def worldToViewport(self, world): # move into local camera space invMat = Mat4(self.cam.getMat(render)) invMat.invertInPlace() local = invMat.xformPoint(world) point = Point2() self.lens.project(local, point) return point def zeroUnusedCoordinate(self, vec): pass def click(self, mask, queue=None, traverser=None, root=None): if not self.mouseWatcher.hasMouse(): return None if not queue: queue = self.clickQueue self.clickRay.setFromLens(self.camNode, self.mouseWatcher.getMouse()) self.clickNode.setFromCollideMask(mask) self.clickNode.setIntoCollideMask(BitMask32.allOff()) self.clickNp.reparentTo(self.cam) queue.clearEntries() if not traverser: base.clickTraverse(self.clickNp, queue) else: if not root: root = self.doc.render traverser.addCollider(self.clickNp, queue) traverser.traverse(root) traverser.removeCollider(self.clickNp) queue.sortEntries() self.clickNp.reparentTo(NodePath()) return queue.getEntries() def fixRatio(self, size=None): if not self.lens: return if size is None: aspectRatio = self.win.getXSize() / self.win.getYSize() else: if size.y > 0: aspectRatio = size.x / size.y else: aspectRatio = 1.0 if self.is2D(): zoomFactor = (1.0 / self.zoom) * 100.0 self.lens.setFilmSize(zoomFactor * aspectRatio, zoomFactor) else: self.lens.setAspectRatio(aspectRatio) if aspectRatio != self.__oldAspectRatio: self.__oldAspectRatio = aspectRatio # Fix up some anything that depends on the aspectRatio if aspectRatio < 1: # If the window is TALL, lets expand the top and bottom self.aspect2d.setScale(1.0, aspectRatio, aspectRatio) self.a2dTop = 1.0 / aspectRatio self.a2dBottom = -1.0 / aspectRatio self.a2dLeft = -1 self.a2dRight = 1.0 else: # If the window is WIDE, lets expand the left and right self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0) self.a2dTop = 1.0 self.a2dBottom = -1.0 self.a2dLeft = -aspectRatio self.a2dRight = aspectRatio # Reposition the aspect2d marker nodes self.a2dTopCenter.setPos(0, 0, self.a2dTop) self.a2dTopCenterNs.setPos(0, 0, self.a2dTop) self.a2dBottomCenter.setPos(0, 0, self.a2dBottom) self.a2dBottomCenterNs.setPos(0, 0, self.a2dBottom) self.a2dLeftCenter.setPos(self.a2dLeft, 0, 0) self.a2dLeftCenterNs.setPos(self.a2dLeft, 0, 0) self.a2dRightCenter.setPos(self.a2dRight, 0, 0) self.a2dRightCenterNs.setPos(self.a2dRight, 0, 0) self.a2dTopLeft.setPos(self.a2dLeft, 0, self.a2dTop) self.a2dTopLeftNs.setPos(self.a2dLeft, 0, self.a2dTop) self.a2dTopRight.setPos(self.a2dRight, 0, self.a2dTop) self.a2dTopRightNs.setPos(self.a2dRight, 0, self.a2dTop) self.a2dBottomLeft.setPos(self.a2dLeft, 0, self.a2dBottom) self.a2dBottomLeftNs.setPos(self.a2dLeft, 0, self.a2dBottom) self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom) self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom) def resizeEvent(self, event): if not self.win: return newsize = LVector2i(event.size().width(), event.size().height()) self.qtWidget.resize(newsize[0], newsize[1]) self.qtWidget.move(0, 0) #props = WindowProperties() #props.setSize(newsize) #props.setOrigin(0, 0) #self.win.requestProperties(props) self.fixRatio(newsize) self.onResize(newsize) self.updateView() def onResize(self, newsize): pass def draw(self): pass def enable(self): # Render to the viewport self.win.setActive(True) self.enabled = True def disable(self): # Don't render to the viewport self.win.setActive(False) self.enabled = False
class Player(DirectObject): def __init__(self, _main): self.main = _main # Stats self.moveSpeed = 8 self.inventory = [] self.maxCarryWeight = 20.0 #kg ? self.currentInventoryWeight = 0.0 # Inventory GUI self.inventoryGui = Inventory() self.inventoryGui.hide() self.inventoryActive = False self.craftInventory = CraftInventory() self.craftInventory.hide() # enable movements through the level self.keyMap = {"left":0, "right":0, "forward":0, "backward":0} self.player = NodePath("Player")#loader.loadModel("smiley") self.player.setPos(149.032, 329.324, 11.3384) self.player.setH(180) self.player.reparentTo(render) self.accept("w", self.setKey, ["forward",1]) self.accept("w-up", self.setKey, ["forward",0]) self.accept("a", self.setKey, ["left",1]) self.accept("a-up", self.setKey, ["left",0]) self.accept("s", self.setKey, ["backward",1]) self.accept("s-up", self.setKey, ["backward",0]) self.accept("d", self.setKey, ["right",1]) self.accept("d-up", self.setKey, ["right",0]) self.accept("mouse1", self.handleLeftMouse) self.accept("i", self.toggleInventory) self.accept("c", self.toggleCraftInventory) # screen sizes self.winXhalf = base.win.getXSize() / 2 self.winYhalf = base.win.getYSize() / 2 self.mouseSpeedX = 0.1 self.mouseSpeedY = 0.1 camera.setH(180) camera.reparentTo(self.player) camera.setZ(self.player, 2) base.camLens.setFov(75) base.camLens.setNear(0.8) # Mouse controls self.mouseNode = CollisionNode('mouseRay') self.mouseNodeNP = camera.attachNewNode(self.mouseNode) self.mouseNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.mouseRay = CollisionRay() self.mouseNode.addSolid(self.mouseRay) self.mouseRayHandler = CollisionHandlerQueue() # Collision Traverser self.traverser = CollisionTraverser("Player Traverser") base.cTrav = self.traverser self.traverser.addCollider(self.mouseNodeNP, self.mouseRayHandler) def run(self): taskMgr.add(self.move, "moveTask", priority=-4) def pause(self): taskMgr.remove("moveTask") def setKey(self, key, value): self.keyMap[key] = value def move(self, task): if not base.mouseWatcherNode.hasMouse(): return task.cont pointer = base.win.getPointer(0) mouseX = pointer.getX() mouseY = pointer.getY() if base.win.movePointer(0, self.winXhalf, self.winYhalf): # calculate the looking up/down of the camera. # NOTE: for first person shooter, the camera here can be replaced # with a controlable joint of the player model p = camera.getP() - (mouseY - self.winYhalf) * self.mouseSpeedY if p <-80: p = -80 elif p > 90: p = 90 camera.setP(p) # rotate the player's heading according to the mouse x-axis movement h = self.player.getH() - (mouseX - self.winXhalf) * self.mouseSpeedX if h <-360: h = 360 elif h > 360: h = -360 self.player.setH(h) # basic movement of the player if self.keyMap["left"] != 0: self.player.setX(self.player, self.moveSpeed * globalClock.getDt()) if self.keyMap["right"] != 0: self.player.setX(self.player, -self.moveSpeed * globalClock.getDt()) if self.keyMap["forward"] != 0: self.player.setY(self.player, -self.moveSpeed * globalClock.getDt()) if self.keyMap["backward"] != 0: self.player.setY(self.player, self.moveSpeed * globalClock.getDt()) # keep the player on the ground elevation = self.main.t.terrain.getElevation(self.player.getX(), self.player.getY()) self.player.setZ(elevation*self.main.t.zScale) return task.cont def toggleInventory(self): if self.inventoryActive: self.inventoryGui.hide() self.inventoryActive = False self.run() else: self.inventoryGui.show() self.inventoryActive = True self.pause() def toggleCraftInventory(self): if self.inventoryActive: self.craftInventory.hide() self.inventoryActive = False self.run() else: self.craftInventory.updateList(self.inventory) self.craftInventory.show() self.inventoryActive = True self.pause() def handleLeftMouse(self): # Do the mining if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.mouseRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) self.traverser.traverse(render) # Assume for simplicity's sake that myHandler is a CollisionHandlerQueue. if self.mouseRayHandler.getNumEntries() > 0: # This is so we get the closest object. self.mouseRayHandler.sortEntries() pickedObj = self.mouseRayHandler.getEntry(0).getIntoNodePath() # Range check if (self.player.getPos() - pickedObj.getPos(render)).length() <= 3.0: self.mine(pickedObj) else: print "You are to far, move closer!" def mine(self, _nodeNP): self.nodeNP = _nodeNP # get the object class for node in self.main.nodeGen.currentNodes: if self.main.nodeGen.currentNodes[node] in self.inventory: print "new Loot:", self.main.nodeGen.currentNodes[node].giveLoot() self.inventory.append(self.main.nodeGen.currentNodes[node]) if self.main.nodeGen.currentNodes[node].lootLeft == 0: self.main.nodeGen.currentNodes[node].removeModel() break break # if mining node else: if self.main.nodeGen.currentNodes[node].model and self.main.nodeGen.currentNodes[node].model.getPos() == self.nodeNP.getPos(render): #self.main.nodeGen.currentNodes[node].removeModel() self.inventory.append(self.main.nodeGen.currentNodes[node]) self.currentInventoryWeight += self.main.nodeGen.currentNodes[node].weight self.inventoryGui.updateList(self.inventory) print "You received:", self.main.nodeGen.currentNodes[node].giveLoot(), self.main.nodeGen.currentNodes[node].giveType(), "Ores" print "Inventory:", self.inventory print "Current Weight:", self.currentInventoryWeight break print self.player.getPos()
class World(DirectObject): mySound = base.loader.loadSfx("trial.mp3") #Used for adding a virus at a randomly generated position def random_vgen(self): print 'I am in random' for i in range(0, 5): self.a[random.randint(0, 7)][random.randint(0, 7)] = 1 def initializer(self, level): self.turns = 0 ## Level Definition def levelone(): self.a[4][4] = 1 self.a[4][5] = 1 self.a[4][6] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[4][6] = 1 self.a[5][5] = 1 def leveltwo(): self.a[4][3] = 1 self.a[4][5] = 1 self.a[4][7] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[2][2] = 1 self.a[5][3] = 1 self.a[1][2] = 1 def levelthree(): self.a[4][4] = 1 self.a[4][5] = 1 self.a[4][6] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[4][6] = 1 self.a[5][5] = 1 self.a[4][3] = 1 self.a[4][5] = 1 self.a[4][7] = 1 self.a[5][2] = 1 self.a[3][5] = 1 self.a[2][2] = 1 self.a[5][3] = 1 options = {1: levelone, 2: leveltwo, 3: levelthree} options[level]() print self.a temp = [] count = 0 for element in reversed(self.a): for ele in element: #print 'cheking element is 1 : ' + str(ele) if ele == 1: temp.append(count) self.pieces[count] = Monster(count, WHITE) #self.squares[count].setColor(HIGHLIGHT) count = count + 1 self.list = temp def showscore(self): try: self.score.destroy() except: pass self.score = OnscreenText(text='Number of Turns : %s' % (self.turns), pos=(0.5, 0.95), scale=0.07, mayChange=True) def menuscreen(self): # Callback function to set text def setText(): b.destroy() helptext.destroy() # Add button b = DirectButton(text=("START", "START", "START", "disabled"), scale=0.15, command=setText, pos=(0, 0, 0.85)) helptext = OnscreenText(text=''' STORY: Hello Mad Scientist! You are so close to finding the cure to aids! You understood the behaviour and can finally manipulate the virus to kill itself! But time is running out! You have a red and white blood cell sample infected with AIDS Virus. INSTRUCTIONS: The AIDS Virus obeys the Conway's Game of Life. Who would have guessed? 1. If virus is surrounded by more than 3 viruses, it dies of suffocation 2. If virus is surrounded by less than 2 viruses, it dies of underpopulation 3. If dead empty cell, is surrounded by exactly 3 viruses, a new virus is spawned due to breeding. AIM: To kill all the viruses, by moving one of them every turn. ''', pos=(-0.90, 0.72), frame=(123, 123, 123, 1), wordwrap=25, align=TextNode.ALeft, bg=(0.23, 0.243, 0.13, 0.9)) def endscreen(self): def restart(): taskMgr.remove(self.mouseTask) for i in range(64): self.squares[i].setColor(SquareColor(i)) scorecard.destroy() restartb.destroy() end.destroy() nextlevelb.destroy() self.reference.destroy() self.mySound.stop() try: self.score.destroy() except: pass print 'restarting' print self.a print self.list World() def quitgame(): sys.exit() def nextLevel(): self.currlevel = self.currlevel + 1 nextlevelb.destroy() scorecard.destroy() restartb.destroy() end.destroy() self.score.destroy() self.initializer(self.currlevel) # Add button scorecard = OnscreenText(text='You finished it in %s turns! ' % (self.turns), frame=(123, 123, 123, 0), wordwrap=25, bg=(0.2, 0, 0.8, 1)) nextlevelb = DirectButton(text=("NEXT LEVEL", "NEXT LEVEL", "NEXT LEVEL", "disabled"), scale=0.15, command=nextLevel, pos=(0, 0, -0.15)) restartb = DirectButton(text=("RESTART", "RESTART", "RESTART", "disabled"), scale=0.15, command=restart, pos=(0, 0, -0.35)) end = DirectButton(text=("QUIT", "QUIT", "QUIT", "disabled"), scale=0.15, command=quitgame, pos=(0, 0, -0.55)) if self.currlevel == self.maxlevel: nextlevelb.destroy() def __init__(self): #music self.mySound.play() print 'I am being initialized' self.menuscreen() self.list = [] self.turns = 0 self.maxlevel = 3 self.currlevel = 1 self.a = [[0 for x in range(8)] for y in range(8)] #This code puts the standard title and instruction text on screen self.title = OnscreenText(text="Game of Life : Help in ending AIDS!", style=1, fg=(1, 1, 1, 1), pos=(0, -0.95), scale=.07) self.escapeEvent = OnscreenText(text="ESC: Quit", style=1, fg=(1, 1, 1, 1), pos=(-1.3, 0.95), align=TextNode.ALeft, scale=.05) ## self.mouse1Event = OnscreenText( ## text="Left-click and drag: Pick up virus and drag it to a new bloodcell", ## style=1, fg=(1,1,1,1), pos=(-1.3, 0.90), ## align=TextNode.ALeft, scale = .05) ## self.accept('escape', sys.exit) #Escape quits base.disableMouse() #Disble mouse camera control camera.setPosHpr(0, -13.75, 6, 0, -25, 0) #Set the camera self.setupLights() #Setup default lighting #Since we are using collision detection to do picking, we set it up like #any other collision detection system with a traverser and a handler self.picker = CollisionTraverser() #Make a traverser self.pq = CollisionHandlerQueue() #Make a handler #Make a collision node for our picker ray self.pickerNode = CollisionNode('mouseRay') #Attach that node to the camera since the ray will need to be positioned #relative to it self.pickerNP = camera.attachNewNode(self.pickerNode) #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it self.pickerNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() #Make our ray self.pickerNode.addSolid(self.pickerRay) #Add it to the collision node #Register the ray as something that can cause collisions self.picker.addCollider(self.pickerNP, self.pq) #self.picker.showCollisions(render) #We will attach all of the squares to their own root. This way we can do the #collision pass just on the sqaures and save the time of checking the rest #of the scene self.squareRoot = render.attachNewNode("squareRoot") #For each square self.squares = [None for i in range(64)] self.pieces = [None for i in range(64)] for i in range(64): #Load, parent, color, and position the model (a single square polygon) self.squares[i] = loader.loadModel("models/square") self.squares[i].reparentTo(self.squareRoot) self.squares[i].setPos(SquarePos1(i)) self.squares[i].setColor(SquareColor(i)) #Set the model itself to be collideable with the ray. If this model was #any more complex than a single polygon, you should set up a collision #sphere around it instead. But for single polygons this works fine. self.squares[i].find("**/polygon").node().setIntoCollideMask( BitMask32.bit(1)) #Set a tag on the square's node so we can look up what square this is #later during the collision pass self.squares[i].find("**/polygon").node().setTag('square', str(i)) #We will use this variable as a pointer to whatever piece is currently #in this square self.initializer(self.currlevel) #This will represent the index of the currently highlited square self.hiSq = False #This wil represent the index of the square where currently dragged piece #was grabbed from self.dragging = False #Start the task that handles the picking self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask') #self.trial = taskMgr.add(self.trial,'trial') self.accept("mouse1", self.grabPiece) #left-click grabs a piece self.accept("mouse1-up", self.releasePiece) #releasing places it #This function swaps the positions of two pieces def swapPieces(self, fr, to): temp = self.pieces[fr] self.pieces[fr] = self.pieces[to] self.pieces[to] = temp if self.pieces[fr]: print 'imma swapping' self.pieces[fr].square = fr print fr print SquarePos(fr) try: self.pieces[fr].obj.setPos(SquarePos(fr)) except: pass print 'done' if self.pieces[to]: self.pieces[to].square = to try: self.pieces[to].obj.setPos(SquarePos(to)) except: pass def trial(self): self.turns = self.turns + 1 try: self.reference.destroy() except: pass self.reference = OnscreenText(text=''' Reference: virus is surrounded by more than 3 viruses, it dies virus is surrounded by less than 2 viruses, it dies If dead empty cell, is surrounded by exactly 3 viruses, a new virus is spawned. ''', style=1, fg=(1, 1, 1, 1), pos=(-1, 0.95), align=TextNode.ALeft, scale=.05) self.showscore() a = self.a while True: for i in self.list: #insert deletion code print 'imma deleting the sq : ' + str(i) self.pieces[i].obj.delete() self.squares[i].setColor(SquareColor(i)) count = 0 a = [[([[ sum(b[y1][x1] for b in [[[( (-1 < x2 + dx < len(a[0])) and (-1 < y2 + dy < len(a))) and a[y2 + dy][x2 + dx] or 0 for x2 in range(len(a[0]))] for y2 in range(len(a))] for (dx, dy) in [(dx, dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] if (dy != 0 or dx != 0)]]) for x1 in range(len(a[0])) ] for y1 in range(len(a))][y][x] == 3 or ([[ sum(c[y3][x3] for c in [[[( (-1 < x4 + dx < len(a[0])) and (-1 < y4 + dy < len(a))) and a[y4 + dy][x4 + dx] or 0 for x4 in range(len(a[0]))] for y4 in range(len(a))] for (dx, dy) in [(dx, dy) for dx in [-1, 0, 1] for dy in [-1, 0, 1] if (dy != 0 or dx != 0)]]) for x3 in range(len(a[0])) ] for y3 in range(len(a))][y][x] == 2 and a[y][x] == 1)) and 1 or 0 for x in range(len(a[0]))] for y in range(len(a))] lis = [] #insert a random virus at a probability of 1/5 diceroll = random.randint(0, 5) if diceroll == 2: self.random_vgen() for element in reversed(a): for ele in element: #print 'cheking element is 1 : ' + str(ele) if ele == 1: lis.append(count) count = count + 1 print lis self.list = lis self.a = a print self.list for i in self.list: self.pieces[i] = Monster(i, WHITE) #self.squares[i].setColor(HIGHLIGHT) print 'leaving trial' if len(self.list) == 0: self.endscreen() break return def mouseTask(self, task): #This task deals with the highlighting and dragging based on the mouse #First, clear the current highlight if self.hiSq is not False: self.squares[self.hiSq].setColor(SquareColor(self.hiSq)) self.hiSq = False #Check to see if we can access the mouse. We need it to do anything else if base.mouseWatcherNode.hasMouse(): #get the mouse position mpos = base.mouseWatcherNode.getMouse() #Set the position of the ray based on the mouse position self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) #If we are dragging something, set the position of the object #to be at the appropriate point over the plane of the board if self.dragging is not False: #Gets the point described by pickerRay.getOrigin(), which is relative to #camera, relative instead to render nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin()) #Same thing with the direction of the ray nearVec = render.getRelativeVector( camera, self.pickerRay.getDirection()) try: self.pieces[self.dragging].obj.setPos( PointAtZ(.5, nearPoint, nearVec)) except: pass #Do the actual collision pass (Do it only on the squares for #efficiency purposes) self.picker.traverse(self.squareRoot) if self.pq.getNumEntries() > 0: #if we have hit something, sort the hits so that the closest #is first, and highlight that node self.pq.sortEntries() i = int(self.pq.getEntry(0).getIntoNode().getTag('square')) #Set the highlight on the picked square self.squares[i].setColor(HIGHLIGHT) self.hiSq = i #print 'selected is ' + str(i) return Task.cont def grabPiece(self): #If a square is highlighted and it has a piece, set it to dragging mode if (self.hiSq is not False and self.pieces[self.hiSq]): self.dragging = self.hiSq self.hiSq = False def releasePiece(self): #Letting go of a piece. If we are not on a square, return it to its original #position. Otherwise, swap it with the piece in the new square if self.dragging is not False: #Make sure we really are dragging something #We have let go of the piece, but we are not on a square if self.hiSq is False: try: self.pieces[self.dragging].obj.setPos( SquarePos(self.dragging)) except: pass else: #Otherwise, swap the pieces self.swapPieces(self.dragging, self.hiSq) #self.draggin is the from print self.list print 'you picked this, so Imma deleting this ' + str( self.dragging) #deletion of i after picking it up. try: self.list.remove(self.dragging) except: pass temp2 = [] temp2 = SquarePosTuple(self.dragging) self.a[temp2[0]][temp2[1]] = 0 i = self.hiSq print self.list print 'highlighted sq is ' + str(i) templis = [] templis = SquarePosTuple(i) print templis self.list.append(i) print self.list print templis self.a[templis[0]][templis[1]] = 1 for line in self.a: print line self.trial() #We are no longer dragging anything self.dragging = False def setupLights(self): #This function sets up some default lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor(Vec4(.8, .8, .8, 1)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection(Vec3(0, 45, -45)) directionalLight.setColor(Vec4(0.2, 0.2, 0.2, 1)) render.setLight(render.attachNewNode(directionalLight)) render.setLight(render.attachNewNode(ambientLight))
class Picker(Viewer): """ View and click objects in a scene.""" def __init__(self): # Parent init. super(Picker, self).__init__() self.disableMouse() # Picker stuff. self.contact_margin = Vec3(0.01, 0.01, 0.01) self.parser = None self.marked = None self.attached_pairs = set() self.contacts = None self.contact_points = None self.contact_bottoms = None self.compound_components = [] self.compound_objects = [] self.joints = JointManager() self.wire_attrib = RenderModeAttrib.make( RenderModeAttrib.MWireframe, 4.) self.attachment_colors = (Vec4(0.1, 0.1, 1., 1.), Vec4(0.1, 0.8, 0.1, 1.), Vec4(1., 0.1, 1., 1.), Vec4(1., 0.5, 0.1, 1.), Vec4(1., 1., 1., 1.)) self.max_attach = 999 self.permanent_events += ["mouse1"] # Make cursor dot. self.cursor = self._build_cursor("cross") s = 0.08 self.cursor.setScale(s, s, s) self.cursor.setColor(1, 1, 1, 1) self.cursor.reparentTo(self.aspect2d) self.taskMgr.add(self.draw_cursor2d, "draw_cursor2d") self.permanent_tasks.append("draw_cursor2d") def init_ssos(self, *args, **kwargs): super(Picker, self).init_ssos(*args, **kwargs) def init_physics(self, *args, **kwargs): super(Picker, self).init_physics(*args, **kwargs) self.joints.bbase = self.bbase def init_picker(self): # Collision traverser self.traverser = CollisionTraverser("traverser") # Collision handler self.handler = CollisionHandlerQueue() # Initialize and set up picker ray node and NodePath self.picker = CollisionNode("mouse_ray") self.pickerNP = self.camera.attachNewNode(self.picker) self.picker.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.picker_ray = CollisionRay() self.picker.addSolid(self.picker_ray) self.traverser.addCollider(self.pickerNP, self.handler) mark_color = (1, 1, 1, 0.3) self.base_mark = self.create_mark(color=mark_color) connector_color = (1, 1, 1, 1) self.base_connector = self.create_connector(color=connector_color) def _build_cursor(self, shape="sphere"): if shape == "sphere": cursor = self._load("sphere.bam") elif shape == "cross": cursor = LineNodePath() lines = [[Point3(-0.5, 0, 0), Point3(0.5, 0, 0)], [Point3(0, 0, -0.5), Point3(0, 0, 0.5)]] cursor.drawLines(lines) cursor.setThickness(1) cursor.create() # cursor = NodePath("cross") # S = {"cylinderX.bam": ((0., 0., 0.), (1., 0.1, 0.1)), # "cylinderY.bam": ((0., 0., 0.), (0.1, 1., 0.1)), # "cylinderZ.bam": ((0., 0., 0.), (0.1, 0.1, 1.))} # for k, v in S.iteritems(): # m = self._load(k) # m.setName(k) # m.setPos(*v[0]) # m.setScale(*v[1]) # m.reparentTo(cursor) #BP() return cursor def create_mark(self, color): """ Makes a graphical mark object.""" # Make a graphical box. props = dict(name="mark", color=color, model="box-round.egg") obj = GSO(props=props) return obj def create_connector(self, color): """ Makes a graphical connector object.""" # Make a graphical box. props = dict(name="connector", color=color, model="connector.egg") obj = GSO(props=props) return obj def start_picker(self, pickables): # Set pickable objs. for i, obj in enumerate(pickables): obj.setTag("pickable", str(i)) # Add mouse events. self.accept("mouse1", self.clicked, extraArgs=[1]) # Start contact detector. detector = ContactDetector(self.bbase.world, self.scene, margin=self.contact_margin) self.contacts = detector.contacts self.contact_bodies = detector.bodies self.contact_points = detector.points parser = Parser(self.contacts, self.contact_bodies) self.contact_bottoms = parser.bottom_bodies self.connectors = {} def stop_picker(self): self.removeTask("mouse1") def goto_sso(self, *args, **kwargs): self.clear_attachments() self.stop_picker() super(Picker, self).goto_sso(*args, **kwargs) self.remove_physics() # Start picker. pickables = self.sso.descendants(type_=PSO) self.start_picker(pickables) self.attach_physics() def get_picked_obj(self): mpos = self.mouseWatcherNode.getMouse() self.picker_ray.setFromLens(self.cam.node(), mpos.getX(), mpos.getY()) self.traverser.traverse(self.render) if self.handler.getNumEntries() > 0: # This is so we get the closest object self.handler.sortEntries() entries = self.handler.getEntries() for entry in entries: picked_obj = entry.getIntoNodePath().findNetTag("pickable") if not picked_obj.isEmpty(): break if picked_obj.isEmpty(): picked_obj = None else: picked_obj = None return picked_obj def clicked(self, button): """ Mouse click handler.""" if self.mouseWatcherNode.hasMouse(): # Get picked object picked_obj = self.get_picked_obj() if picked_obj is not None: if self.marked is None: # New mark activated. self.marked = picked_obj self.show_marked(picked_obj, True) event = "mark" elif picked_obj == self.marked: # Existing mark deactivated. self.show_marked(picked_obj, False) self.marked = None event = "unmark" else: # New attachment or detachment. pair = tuple(sorted((self.marked, picked_obj))) ij = tuple(sorted((self.contact_bodies.index(pair[0]), self.contact_bodies.index(pair[1])))) if ij in self.contacts: f_add = (ij, pair) not in self.attached_pairs if (not f_add or len(self.attached_pairs) < self.max_attach): self.store_attachment(ij, pair, f_add) self.show_marked(self.marked, False) self.marked = None event = "attach" if f_add else "detach" else: print("Max attachments already reached.") event = "max-attach" else: event = "non-contact" else: event = "non-pick" return picked_obj, event def store_attachment(self, ij, pair, f_add): """ Stores the attached objects, and draws them.""" if f_add: self.attached_pairs.add((ij, pair)) self.show_attachment(ij, True) self.attach_pair(pair, True) else: try: self.attached_pairs.remove((ij, pair)) except KeyError: pass else: self.attach_pair(pair, False) self.show_attachment(ij, False) def clear_attachments(self): """ Clear all attachments.""" if self.marked: self.show_marked(self.marked, False) self.marked = None self.mark = None for ij, pair in self.attached_pairs: self.attach_pair(pair, False) self.show_attachment(ij, False) self.attached_pairs = set() # self.reset_compounds() self.contacts = None self.contact_bodies = None self.contact_points = None self.contact_bottoms = None def _make_mark(self, node, extent, name): """ Makes a mark GSO.""" mark = self.base_mark.copy() mat = node.getMat(self.scene) mark.apply_prop(dict(name=name), other=self.scene) mark.setMat(self.scene, mat) mark.setScale(self.scene, mark.getScale(self.scene) + extent) mark.wrtReparentTo(node) return mark def show_marked(self, node, f_on): """ Turns on/off marked graphic.""" if f_on: extent = Vec3(0.15, 0.15, 0.15) name = "mark" self.mark = self._make_mark(node, extent, name) self.mark.init_tree(tags=("model",)) # Exclude object from casting shadows self.mark.hide(self.shadow_mask) self.mark.setTransparency(TransparencyAttrib.MAlpha) self.mark.setDepthWrite(False) self.mark.setBin("fixed", 0, priority=5) else: self.mark.removeNode() def _make_connector(self, parent, points, extent, name): """ Makes connector object.""" connector = self.base_connector.copy() scale = Vec3(*(np.ptp(points, axis=0))) scale_extended = scale + extent pos = Point3(*(np.min(points, axis=0) + scale / 2.)) connector.apply_prop(dict(name=name, scale=scale_extended, pos=pos), other=self.scene) connector.wrtReparentTo(parent) return connector def show_attachment(self, ij, f_on): """ Turns on/off attachment graphic.""" if f_on: parent = self.contact_bottoms[ij] points = self.contact_points[ij] extent = Vec3(0.15, 0.15, 0.15) name = "connector_%d-%d" % ij self.connectors[ij] = self._make_connector(parent, points, extent, name) self.connectors[ij].init_tree(tags=("model",)) else: self.connectors.pop(ij).removeNode() # def attach_pair(self, pair, f_on): # """ Adds/removes physical attachment between a pair of nodes.""" # key = tuple(sorted(p.node() for p in pair)) # # key = frozenset(pair) # if f_on: # # Create the joint and add it. # self.joints[key] = self.joints.make_fixed(*pair) # else: # # Remove it. # del self.joints[key] def attach_physics(self): # Attach `self.scene` to the physics world. try: exclude = zip(*self.compound_components)[0] except IndexError: exclude = [] bnodes = [bnode for bnode in self.scene.descendants(type_=PSO) if bnode not in exclude] for bnode in bnodes: bnode.init_resources(tags=("shape",)) bnode.setCollideMask(BitMask32.allOn()) bnode.node().setDeactivationEnabled(False) self.bbase.attach(bnodes) def reset_compounds(self): for n, p in self.compound_components: n.wrtReparentTo(p) self.compound_components = [] for cnode in self.compound_objects: cnode.destroy_resources() cnode.removeNode() self.compound_objects = [] def make_attachment_graph(self): if not self.contact_bodies: return None n = len(self.contact_bodies) mtx = np.zeros((n, n), dtype="i") for (i, j), _ in self.attached_pairs: # i = self.contact_bodies.index(pair[0]) # j = self.contact_bodies.index(pair[1]) mtx[i, j] = 1 # mtx[j, i] = 1 graph = nx.from_numpy_matrix(mtx) return graph def attach_pair(self, pair, f_on): """ Adds/removes physical attachment between a pair of nodes.""" # Get the connected subgroups. graph = self.make_attachment_graph() sgs = [sg for sg in nx.connected_components(graph) if len(sg) > 1] self.reset_compounds() # Iterate over subgroups, creating compound shapes. for sg in sgs: nodes = [self.contact_bodies[i] for i in sg] parents = [c.getParent() for c in nodes] self.compound_components.extend(zip(nodes, parents)) cname = "+".join([str(i) for i in sorted(sg)]) cnode = CPSO(cname) cnode.reparentTo(self.scene) cnode.add(nodes) cnode.init_tree(tags=("shape",)) cnode.destroy_component_shapes() self.compound_objects.append(cnode)
class Grabber(object): def __init__( self, levitNP): """ A widget to position, rotate, and scale Panda 3D Models and Actors * handleM1 decides what to do with a mouse1 click -- object selection by calling handleSelection when the grabModel is inactive (hidden) -- object manipulation by calling handleManipulationSetup (sets the stage for and launches the dragTask) isHidden() when nothing is selected isDragging means not running collision checks for selection setup and LMB is pressed call handleM1 from another class to push control up in the program hierarchy (remove inner class calls) """ # TODO remove selection functionality from grabber and put it in a selector class self.levitorNP = levitNP # TODO remove this and use barebonesNP self.selected = None self.initialize() def initialize(self): """Reset everything except LevitorNP and selected, also called inside __init__""" self.notify = DirectNotify().newCategory('grabberErr') self.currPlaneColNorm = Vec3(0.0) self.isCameraControlOn = False self.isDragging = False self.isMultiselect = False self.grabScaleFactor = .075 self.currTransformDir = Point3(0.0) self.interFrameMousePosition = Point3(0.0) self.init3DemVal = Point3(0.0) # initCommVal holds the value before a command operation has taken place self.initialCommandTrgVal = None # To load the grabber model, this climbs up the absolute path to /barebones/ to gain access to the model folder self.grabModelNP = loader.loadModel(Filename.fromOsSpecific( ntpath.split( ntpath.split(inspect.stack()[0][1])[0] )[0]) + '/EditorModels/widget') self.grabModelNP.setPos(0.0, 0.0, 0.0) self.grabModelNP.setBin("fixed", 40) self.grabModelNP.setDepthTest(False) self.grabModelNP.setDepthWrite(False) self.transformOpEnum = Enum('rot, scale, trans') self.currTransformOperation = None # TODO For readability, use this enum in the nested if/else as was the original intent. self.grabInd = Enum('xRot, yRot, zRot, xScaler, yScaler, zScaler, xTrans, yTrans, zTrans, xyTrans, xzTrans, zyTrans, grabCore') grbrNodLst = [self.grabModelNP.find("**/XRotator;+h-s-i"), # 0 self.grabModelNP.find("**/YRotator;+h-s-i"), # 1 self.grabModelNP.find("**/ZRotator;+h-s-i"), # 2 end rotate self.grabModelNP.find("**/XScaler;+h-s-i"), # 3 self.grabModelNP.find("**/YScaler;+h-s-i"), # 4 self.grabModelNP.find("**/ZScaler;+h-s-i"), # 5 end scale self.grabModelNP.find("**/XTranslator;+h-s-i"), # 6 self.grabModelNP.find("**/YTranslator;+h-s-i"), # 7 self.grabModelNP.find("**/ZTranslator;+h-s-i"), # 8 end translate / end single dir operations self.grabModelNP.find("**/XYTranslator;+h-s-i"), # 9 self.grabModelNP.find("**/XZTranslator;+h-s-i"), # 10 self.grabModelNP.find("**/ZYTranslator;+h-s-i"), # 11 end bi-directional operations self.grabModelNP.find("**/WidgetCore;+h-s-i")] # 12 #Mat4.yToZUpMat() # change coordinate to z up grbrNodLst[12].getParent().setHprScale(0, 0, 0, 1, 1, -1) self.grabModelNP.setPythonTag('grabberRoot', grbrNodLst) self.grabModelNP.reparentTo(BBGlobalVars.bareBonesObj.levitorNP) self.grabModelNP.hide() #self.grabIntoBitMask = COLLISIONMASKS self.grabModelNP.setCollideMask(COLLISIONMASKS['default']) self.grabModelNP.setPythonTag('grabber', self) ############################################################################## # This whole section is the basics for setting up mouse selection # --The mouse events are added in the events section (next) # Create the collision node for the picker ray to add traverser as a 'from' collider self.grabberColNode = CollisionNode('grabberMouseRay') # Set the collision bitmask # TODO: define collision bitmask (let user define thiers? likely not) self.defaultBitMask = GeomNode.getDefaultCollideMask() self.grabberColNode.setFromCollideMask(self.defaultBitMask) self.grabberRayColNP = camera.attachNewNode(self.grabberColNode) # Create the grabberRay and add it to the picker CollisionNode self.grabberRay = CollisionRay(0.0, 0.0, 0.0, 0.0, 1.0, 0.0) self.grabberRayNP = self.grabberColNode.addSolid(self.grabberRay) # create a collision queue for the traverser self.colHandlerQueue = CollisionHandlerQueue() # Create collision traverser self.colTraverser = CollisionTraverser('grabberTraverser') # Set the collision traverser's 'fromObj' and handler # e.g. trav.addCollider( fromObj, handler ) self.colTraverser.addCollider(self.grabberRayColNP, self.colHandlerQueue) ############################################################ # setup event handling with the messenger # URGENT remove all of this messenger code throughout Grabber, especially the camera control # disable the mouse when the ~ is pressed (w/o shift) self.disableCamera() # disable camera control by the mouse messenger.accept('`', self, self.enableCamera) # enable camera control when the ~ key is pressed w/o shift messenger.accept('`-up', self, self.disableCamera) # disable camera control when the ~ key is released # handle mouse selection/deselection & manipulating the scene messenger.accept('mouse1', self, self.handleM1, persistent=1) # deselect in event handler taskMgr.add(self.scaleGrabber, 'scaleGrabber') # //////////////////////////////////////////////////////////////////// # comment out: good for debug info #taskMgr.add(self.watchMouseColl, name='grabberDebug') #this is only good for seeing types and hierarchy #self.grabModelNP.ls() #render.ls() # self.frames = 0 #remove # self.axis = loader.loadModel("zup-axis") # self.axis.reparentTo(self.grabModelNP) # self.axis.setScale(.15) # self.axis.setPos(0.0) # self.grabModelNP.append( 'newAttrib', self) # setattr( self.grabModelNP, 'newAttrib', self) def prepareForPickle(self): self.colTraverser = None # Traversers are not picklable self.defaultBitMask = None # BitMasks "..." # self.grabIntoBitMask = None # "..." self.colHandlerQueue = None # CollisonHandlerQueue "..." self.grabModelNP.removeNode() self.grabModelNP = None taskMgr.remove('scaleGrabber') def recoverFromPickle(self): self.initialize() if self.selected is not None: self.grabModelNP.setPos(render, self.selected.getPos(render)) self.grabModelNP.show() print "grabber sel ", self.selected, " isHidden() ", self.grabModelNP.isHidden(), '\n' taskMgr.add(self.scaleGrabber, 'scaleGrabber') #### May use to gain control over pickling. # def __repr__(self): # for pickling purposes # if self.colTraverser: # self.colTraverser = None # # dictrepr = dict.__repr__(self.__dict__) # dictrepr = '%r(%r)' % (type(self).__name__, dictrepr) # print dictrepr # REMOVE # return dictrepr def watchMouseColl(self, task): """ This exists for debugging purposes to perpetually watch mouse collisions. """ # TODO make this highlight objects under the mouse for predictable object selection/grabber operations self.colTraverser.showCollisions(render) if base.mouseWatcherNode.hasMouse() and False == self.isCameraControlOn: # This gives the screen coordinates of the mouse. mPos = base.mouseWatcherNode.getMouse() # This makes the ray's origin the camera and makes the ray point # to the screen coordinates of the mouse. self.grabberRay.setFromLens(base.camNode, mPos.getX(), mPos.getY()) # traverses the graph for collisions self.colTraverser.traverse(render) return task.cont def scaleGrabber(self, task): if self.grabModelNP.isHidden(): return task.cont coreLst = self.grabModelNP.getPythonTag('grabberRoot') camPos = self.grabModelNP.getRelativePoint(self.grabModelNP, camera.getPos()) if camPos.z >= 0: # 1-4 if camPos.x > 0.0 <= camPos.y: # quad 1 coreLst[12].getParent().setScale( 1, 1, -1) elif camPos.x < 0.0 <= camPos.y: # quad 2 coreLst[12].getParent().setScale( -1, 1, -1) elif camPos.x < 0.0 >= camPos.y: # quad 3 coreLst[12].getParent().setScale( -1, -1, -1) elif camPos.x > 0.0 >= camPos.y: # quad 4 coreLst[12].getParent().setScale( 1, -1, -1) else: self.notify.warning("if-else default, scaleGrabber cam.z > 0") else: # 5-8 if camPos.x > 0.0 <= camPos.y: # quad 5 coreLst[12].getParent().setScale( 1, 1, 1) elif camPos.x < 0.0 <= camPos.y: # quad 6 coreLst[12].getParent().setScale( -1, 1, 1) elif camPos.x < 0.0 >= camPos.y: # quad 7 coreLst[12].getParent().setScale( -1, -1, 1) elif camPos.x > 0.0 >= camPos.y: # quad 8 coreLst[12].getParent().setScale( 1, -1, 1) else: self.notify.warning("if-else default, scaleGrabber cam.z z < 0") distToCam = (camera.getPos() - render.getRelativePoint(BBGlobalVars.currCoordSysNP, self.grabModelNP.getPos())).length() self.grabModelNP.setScale(self.grabScaleFactor * distToCam, self.grabScaleFactor * distToCam, self.grabScaleFactor * distToCam) # keep the position identical to the selection # for when outside objects like undo/redo move selected self.grabModelNP.setPos(render, self.selected.getPos(render)) return task.cont # TODO find a way to move camera control to a proper camera handler, perhaps move these to a global def enableCamera(self): self.isCameraControlOn = True PanditorEnableMouseFunc() def disableCamera(self): self.isCameraControlOn = False PanditorDisableMouseFunc() def handleM3(self): """Deselect the selected object.""" if not self.grabModelNP.isHidden() and not self.isCameraControlOn: # if the grab model is in the scene and the camera is not in control if base.mouseWatcherNode.hasMouse() and not self.isDragging: # we're ignoring accidental mouse3 clicks while dragging here with not isDragging self.selected = None # empty the selected, will be turned back on once something's selected messenger.ignore('mouse3', self) # turn the deselect event off self.grabModelNP.hide() # hide the grab model and set it back to render's pos self.grabModelNP.setPos(0.0) def handleM1Up(self): """Stop dragging the selected object.""" taskMgr.remove('mouse1Dragging') self.isDragging = False self.currTransformOperation = None # NOTE other references have been added, but no other object references them # record the mouse1 operation BBGlobalVars.undoHandler.record(self.selected, CommandUndo([self.initialCommandTrgVal], self.selected.setMat, self.selected.getMat(render))) messenger.ignore('mouse1-up', self) def handleM1(self): """Decides how to handle a mouse1 click.""" if self.isCameraControlOn: return if base.mouseWatcherNode.hasMouse(): # give the grabber first chance if self.grabModelNP.isHidden(): # no collisions w/ grabber or nothing selected # handle selection with scene objects self.handleSelection() elif not self.isDragging: # The grabber is in place but not dragging. Get ready to drag. self.handleManipulationSetup() # it'll call self.handleSelection() if no collision w/ grabber # TODO (if warranted) make self.handleManipulationSetup() return false if no col w/ grabber, call selection here instead def handleManipulationSetup(self): """Sets up all the attributes needed for the mouse dragging task.""" # This makes the ray's origin the camera and makes the ray point # to the screen coordinates of the mouse. if self.isDragging: return camVec = self.grabModelNP.getRelativeVector(self.grabModelNP, camera.getPos()) mPos = base.mouseWatcherNode.getMouse() self.grabberRay.setFromLens(base.camNode, mPos.getX(), mPos.getY()) self.colTraverser.traverse(self.grabModelNP) # look for collisions on the grabber if not self.isCameraControlOn and self.colHandlerQueue.getNumEntries() > 0 and not self.grabModelNP.isHidden(): # see if collided with the grabber if not handle re or multi selection self.colHandlerQueue.sortEntries() grabberObj = self.colHandlerQueue.getEntry(0).getIntoNodePath() grabberLst = self.grabModelNP.getPythonTag('grabberRoot') # see __init__ # the index gives the operations rot < 3 scale < 6 trans < 9 trans2D < 12 # mod index gives axis 0 == x, 1 == y, 2 == z ind = -1 for i in range(0, 13): if grabberObj == grabberLst[i]: ind = i grabberObj = grabberLst[i] # ensure we are not picking ourselves, ahem, the grabber assert(not self.grabModelNP.isAncestorOf(self.selected)) mPos3D = Point3(0.0) xVec = Vec3(1, 0, 0) yVec = Vec3(0, 1, 0) zVec = Vec3(0, 0, 1) # TODO: ??? break this up into translate rotate and scale function to make it readable if -1 < ind < 3: # rotate if ind % 3 == 0: # x self.initializeManipVars(Point3(1.0, 0.0, 0.0), self.transformOpEnum.rot, Point3(mPos.getX(), mPos.getY(), 0.0)) elif ind % 3 == 1: # y self.initializeManipVars(Point3(0.0, 1.0, 0.0), self.transformOpEnum.rot, Point3(mPos.getX(), mPos.getY(), 0.0)) else: # z self.initializeManipVars(Point3(0.0, 0.0, 1.0), self.transformOpEnum.rot, Point3(mPos.getX(), mPos.getY(), 0.0)) elif ind < 6: # scale if ind % 3 == 0: # x self.initializeManipVars(Point3(1.0, 0.0, 0.0), self.transformOpEnum.scale, Point3(mPos.getX(), mPos.getY(), 0.0)) elif ind % 3 == 1: # y # self.currTransformDir = Point3( 0.0, 1.0, 0.0) self.initializeManipVars(Point3(0.0, 1.0, 0.0), self.transformOpEnum.scale, Point3(mPos.getX(), mPos.getY(), 0.0)) else: # z # self.currTransformDir = Point3( 0.0, 0.0, 1.0) self.initializeManipVars(Point3(0.0, 0.0, 1.0), self.transformOpEnum.scale, Point3(mPos.getX(), mPos.getY(), 0.0)) elif ind < 9: # translate if ind % 3 == 0: # x # if the camera's too flat to the collision plane bad things happen if camVec.angleDeg( zVec) < 89.0 and self.getMousePlaneIntersect(mPos3D, zVec): self.initializeManipVars(Point3(1.0, 0.0, 0.0), self.transformOpEnum.trans, mPos3D, zVec) elif self.getMousePlaneIntersect(mPos3D, yVec): self.initializeManipVars(Point3(1.0, 0.0, 0.0), self.transformOpEnum.trans, mPos3D, yVec) elif ind % 3 == 1: # y if camVec.angleDeg( zVec) < 89.0 and self.getMousePlaneIntersect(mPos3D, zVec): self.initializeManipVars(Point3(0.0, 1.0, 0.0), self.transformOpEnum.trans, mPos3D, zVec) elif self.getMousePlaneIntersect(mPos3D, xVec): self.initializeManipVars(Point3(0.0, 1.0, 0.0), self.transformOpEnum.trans, mPos3D, xVec) else: # z if camVec.angleDeg( yVec) < 89.0 and self.getMousePlaneIntersect(mPos3D, yVec): self.initializeManipVars(Point3(0.0, 0.0, 1.0), self.transformOpEnum.trans, mPos3D, yVec) elif self.getMousePlaneIntersect(mPos3D, xVec): self.initializeManipVars(Point3(0.0, 0.0, 1.0), self.transformOpEnum.trans, mPos3D, xVec) elif ind < 12: # translate 2D if ind % 3 == 0: # xy if self.getMousePlaneIntersect(mPos3D, zVec): self.initializeManipVars(Point3(1.0, 1.0, 0.0), self.transformOpEnum.trans, mPos3D, zVec) elif ind % 3 == 1: # xz if self.getMousePlaneIntersect(mPos3D, yVec): self.initializeManipVars(Point3(1.0, 0.0, 1.0), self.transformOpEnum.trans, mPos3D, yVec) else: # zy if self.getMousePlaneIntersect(mPos3D, xVec): self.initializeManipVars(Point3(0.0, 1.0, 1.0), self.transformOpEnum.trans, mPos3D, xVec) elif ind == 12: # scale in three directions self.initializeManipVars(Point3(1.0, 1.0, 1.0), self.transformOpEnum.scale, Point3(mPos.getX(), mPos.getY(), 0.0)) else: self.notify.warning("Grabber Err: no grabber collision when col entries > 0 AND grabber not hidden") # Save initial value for save/undo. # The end result of the operation is sent to the undo handler on mouse up event. if self.selected: self.initialCommandTrgVal = self.selected.getMat(render) else: # no collisions w/ grabber or nothing selected # handle reselection or multi-selection (not yet implemented) with other scene obj self.handleSelection() def handleSelection(self): if self.isDragging: return # First check that the mouse is not outside the screen. if base.mouseWatcherNode.hasMouse() and False == self.isCameraControlOn: self.grabberColNode.setFromCollideMask(self.defaultBitMask) # This gives the screen coordinates of the mouse. mPos = base.mouseWatcherNode.getMouse() # This makes the ray's origin the camera and makes the ray point # to the screen coordinates of the mouse. self.colHandlerQueue.clearEntries() self.grabberRay.setFromLens(base.camNode, mPos.getX(), mPos.getY()) self.colTraverser.traverse(render) # look for collisions if self.colHandlerQueue.getNumEntries() > 0: self.colHandlerQueue.sortEntries() grabbedObj = self.colHandlerQueue.getEntry(0).getIntoNodePath() if not grabbedObj.findNetTag('pickable').isEmpty(): grabbedObj = grabbedObj.findNetTag('pickable') self.selected = grabbedObj self.grabModelNP.setPos(render, grabbedObj.getPos(render).x, grabbedObj.getPos(render).y, grabbedObj.getPos(render).z) self.grabModelNP.show() messenger.accept('mouse3', self, self.handleM3) def handleDragging(self, task): """ Does the actual work of manipulating objects, once the needed attributes have been setup by handleManipulationSetup(). """ if not self.isDragging: return task.done mPos3D = Point3(0.0) # # This section handles the actual translating rotating or scale after it's been set up in mouse1SetupManip...() # ONLY one operation is preformed per frame if self.currTransformOperation == self.transformOpEnum.trans: # 1st translation, rotation's section is at next elif if self.getMousePlaneIntersect(mPos3D, self.currPlaneColNorm): # get the difference between the last mouse and this frames mouse selectedNewPos = mPos3D - self.interFrameMousePosition # store this frames mouse self.interFrameMousePosition = mPos3D # add the difference to the selected object's pos self.selected.setPos(render, self.selected.getPos(render).x + self.currTransformDir.x * selectedNewPos.x, self.selected.getPos(render).y + self.currTransformDir.y * selectedNewPos.y, self.selected.getPos(render).z + self.currTransformDir.z * selectedNewPos.z) self.grabModelNP.setPos(render, self.selected.getPos(render)) elif self.currTransformOperation == self.transformOpEnum.rot: # 2nd rotation, followed finally by scaling # if operating on the z-axis, use the y (vertical screen coordinates otherwise use x (horizontal) mPos = base.mouseWatcherNode.getMouse() #rotMag = 0.0 if self.currTransformDir == Vec3( 0.0, 0.0, 1.0): rotMag = (mPos.x - self.interFrameMousePosition.x) * 1000 else: rotMag = (self.interFrameMousePosition.y - mPos.y) * 1000 initPos = self.selected.getPos() initPar = self.selected.getParent() self.selected.wrtReparentTo(render) self.selected.setMat(self.selected.getMat() * Mat4.rotateMat(rotMag, self.currTransformDir)) self.selected.wrtReparentTo(initPar) self.selected.setPos(initPos) self.interFrameMousePosition = Point3(mPos.x, mPos.y, 0.0) elif self.currTransformOperation == self.transformOpEnum.scale: # 3rd and final is scaling mPos = base.mouseWatcherNode.getMouse() # TODO: make dragging away from the object larger and to the object smaller (not simply left right up down) # td The problem with this MAY come if negative, mirrored, scaling is implemented. # if operating on the z-axis, use the y (vertical screen coordinates otherwise use x (horizontal) if self.currTransformDir == Point3( 0.0, 0.0, 1.0): sclMag = (mPos.y - self.interFrameMousePosition.y) * 5.5 elif self.currTransformDir == Point3( 0.0, 1.0, 0.0): sclMag = (mPos.x - self.interFrameMousePosition.x) * 5.5 else: sclMag = (self.interFrameMousePosition.x - mPos.x) * 5.5 # This is the line that prevents scaling past the origin. Flipping the faces doesn't seem to work. if -0.0001 < sclMag < 0.0001: sclMag = 0.000001 # create a dummy node to parent to and position such that applying scale to it will scale selected properly dummy = self.levitorNP.attachNewNode('dummy') initScl = dummy.getScale() # Don't forget the parent. Selected needs put back in place initPar = self.selected.getParent() initPos = self.selected.getPos() self.selected.wrtReparentTo(dummy) dummy.setScale(initScl.x + sclMag * self.currTransformDir.x, initScl.y + sclMag * self.currTransformDir.y, initScl.z + sclMag * self.currTransformDir.z) # reset selected's parent then destroy dummy self.selected.wrtReparentTo(initPar) self.selected.setPos(initPos) dummy.removeNode() dummy = None self.interFrameMousePosition = Point3( mPos.x, mPos.y, 0.0) else: self.notify.error("Err: Dragging with invalid curTransformOperation enum in handleDragging") return task.cont # ended by handleM1Up(), the mouse1-up event handler def initializeManipVars(self, transformDir, transformOp, mPos3D, planeNormVec=None): self.currTransformDir = transformDir self.currPlaneColNorm = planeNormVec # set the norm for the collision plane to be used in mouse1Dragging self.interFrameMousePosition = mPos3D self.currTransformOperation = transformOp self.isDragging = True taskMgr.add(self.handleDragging, 'mouse1Dragging') messenger.accept('mouse1-up', self, self.handleM1Up) def getMousePlaneIntersect(self, mPos3Dref, normVec): mPos = base.mouseWatcherNode.getMouse() plane = Plane(normVec, self.grabModelNP.getPos()) nearPoint = Point3() farPoint = Point3() base.camLens.extrude(mPos, nearPoint, farPoint) if plane.intersectsLine(mPos3Dref, render.getRelativePoint(camera, nearPoint), render.getRelativePoint(camera, farPoint)): return True return False def destroy(self): raise NotImplementedError('Make sure messenger etc are cleared of refs and the model node is deleted') self.grabModelNP.clearPythonTag('grabberRoot') self.grabModelNP.clearPythonTag('grabber') self.grabModelNP = None messenger.ignoreAll(self)
class CameraShyFirstPerson(FirstPerson): toonInFocusColor = VBase4(0.25, 1.0, 0.25, 1.0) toonOutOfFocusColor = VBase4(1.0, 1.0, 1.0, 1.0) fullyChargedState = 5 def __init__(self, mg): self.mg = mg self.cameraFocus = None self.batteryFrame = None self.batteryBg = None self.batteryBar = None self.rechargeSound = None self.fullyChargedSound = None self.hasToonInFocus = False self.toonToTakePicOf = None self.cameraRechargeState = None self.cameraRechargingLabel = None self.cameraFlashSeq = None self.camFSM = ClassicFSM('CameraFSM', [State('off', self.enterOff, self.exitOff), State('ready', self.enterCameraReady, self.exitCameraReady), State('recharge', self.enterCameraRecharge, self.exitCameraRecharge)], 'off', 'off') self.camFSM.enterInitialState() FirstPerson.__init__(self) return def movementTask(self, task): if not inputState.isSet('jump') and not base.localAvatar.walkControls.isAirborne and inputState.isSet('forward') or inputState.isSet('reverse') or inputState.isSet('slideLeft') or inputState.isSet('slideRight'): if base.localAvatar.getAnimState() != 'run': base.localAvatar.setAnimState('run') base.localAvatar.playMovementSfx('run') self.mg.sendUpdate('runningAvatar', [base.localAvatar.doId]) elif inputState.isSet('jump') or base.localAvatar.walkControls.isAirborne: if base.localAvatar.getAnimState() != 'jump': base.localAvatar.setAnimState('jump') base.localAvatar.playMovementSfx(None) self.mg.sendUpdate('jumpingAvatar', [base.localAvatar.doId]) elif base.localAvatar.getAnimState() != 'neutral': base.localAvatar.setAnimState('neutral') base.localAvatar.playMovementSfx(None) self.mg.sendUpdate('standingAvatar', [base.localAvatar.doId]) return task.cont def enterOff(self): pass def exitOff(self): pass def enterCameraReady(self): self.acceptOnce('mouse1', self.__mouse1Pressed) def stopCameraFlash(self): if self.cameraFlashSeq: self.cameraFlashSeq.finish() self.cameraFlashSeq = None return def __mouse1Pressed(self): self.cameraFlashSeq = Sequence(Func(base.transitions.setFadeColor, 1, 1, 1), Func(base.transitions.fadeOut, 0.1), Wait(0.1), Func(base.transitions.fadeIn, 0.1), Wait(0.1), Func(base.transitions.setFadeColor, 0, 0, 0)) self.cameraFlashSeq.start() self.mg.sendUpdate('remoteAvatarTakePicture', [base.localAvatar.doId]) self.mg.myRemoteAvatar.takePicture() if self.hasToonInFocus and self.toonToTakePicOf: self.mg.sendUpdate('tookPictureOfToon', [self.toonToTakePicOf.doId]) self.camFSM.request('recharge') def exitCameraReady(self): self.ignore('mouse1') def enterCameraRecharge(self): self.batteryBar.update(0) taskMgr.add(self.__rechargeNextState, 'rechargeCamera') def __rechargeNextState(self, task): if self.cameraRechargeState == None: self.cameraRechargeState = -1 self.cameraRechargeState += 1 if self.cameraRechargeState > 0: base.playSfx(self.rechargeSound) self.batteryBar.update(self.cameraRechargeState) if self.cameraRechargeState == self.fullyChargedState: base.playSfx(self.fullyChargedSound) self.camFSM.request('ready') return task.done else: task.delayTime = 1.0 return task.again def exitCameraRecharge(self): taskMgr.remove('rechargeCamera') self.cameraRechargeState = None return def __handleRayInto(self, entry): intoNP = entry.getIntoNodePath() toonNP = intoNP.getParent() for key in base.cr.doId2do.keys(): obj = base.cr.doId2do[key] if obj.__class__.__name__ == 'DistributedToon': if obj.getKey() == toonNP.getKey(): self.__handleToonInFocus(obj) def __handleRayOut(self, entry): intoNP = entry.getIntoNodePath() toonNP = intoNP.getParent() for key in base.cr.doId2do.keys(): obj = base.cr.doId2do[key] if obj.__class__.__name__ == 'DistributedToon': if obj.getKey() == toonNP.getKey(): self.toonToTakePicOf = None self.hasToonInFocus = False if self.cameraFocus.getColorScale() == self.toonInFocusColor: self.cameraFocus.setColorScale(self.toonOutOfFocusColor) return def __handleToonInFocus(self, toon): if not self.hasToonInFocus or self.toonToTakePicOf is not None or self.toonToTakePicOf.doId != toon.doId: self.toonToTakePicOf = toon self.hasToonInFocus = True self.cameraFocus.setColorScale(self.toonInFocusColor) return def start(self): self.fullyChargedSound = base.loadSfx('phase_4/audio/sfx/MG_pairing_match.mp3') self.rechargeSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_blue_arrow.mp3') self.batteryFrame = DirectFrame(parent=base.a2dBottomRight, pos=(-0.2, 0, 0.1), scale=(0.8, 0, 1)) self.batteryBg = OnscreenImage(image='phase_4/maps/battery_charge_frame.png', parent=self.batteryFrame) self.batteryBg.setTransparency(1) self.batteryBg.setX(0.03) self.batteryBg.setScale(0.17, 0, 0.05) self.batteryBar = DirectWaitBar(value=0, range=5, barColor=(1, 1, 1, 1), relief=None, scale=(0.12, 0.0, 0.3), parent=self.batteryFrame) self.cameraFocus = loader.loadModel('phase_4/models/minigames/photo_game_viewfinder.bam') self.cameraFocus.reparentTo(base.aspect2d) self.focusCollHandler = CollisionHandlerEvent() self.focusCollHandler.setInPattern('%fn-into') self.focusCollHandler.setOutPattern('%fn-out') self.focusCollNode = CollisionNode('mouseRay') self.focusCollNP = base.camera.attachNewNode(self.focusCollNode) self.focusCollNode.setCollideMask(BitMask32(0)) self.focusCollNode.setFromCollideMask(CIGlobals.WallBitmask) self.focusRay = CollisionRay() self.focusRay.setFromLens(base.camNode, 0.0, 0.0) self.focusCollNode.addSolid(self.focusRay) base.cTrav.addCollider(self.focusCollNP, self.focusCollHandler) base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed, 0.0, CIGlobals.ToonReverseSpeed, CIGlobals.ToonRotateSpeed) FirstPerson.start(self) return def reallyStart(self): self.accept('mouseRay-into', self.__handleRayInto) self.accept('mouseRay-out', self.__handleRayOut) self.camFSM.request('recharge') taskMgr.add(self.movementTask, 'movementTask') FirstPerson.reallyStart(self) def end(self): self.camFSM.request('off') taskMgr.remove('movementTask') self.ignore('mouseRay-into') self.ignore('mouseRay-out') FirstPerson.end(self) def reallyEnd(self): self.batteryBar.destroy() self.batteryBar = None self.batteryBg.destroy() self.batteryBg = None self.batteryFrame.destroy() self.batteryFrame = None self.cameraFocus.removeNode() self.cameraFocus = None self.focusCollHandler = None self.focusCollNode = None self.focusCollNP.removeNode() self.focusCollNP = None self.focusRay = None self.hasToonInFocus = None self.toonToTakePicOf = None self.fullyChargedSound = None self.rechargeSound = None self.stopCameraFlash() FirstPerson.reallyEnd(self) base.localAvatar.walkControls.setWalkSpeed(CIGlobals.ToonForwardSpeed, CIGlobals.ToonJumpForce, CIGlobals.ToonReverseSpeed, CIGlobals.ToonRotateSpeed) return def cleanup(self): self.camFSM.requestFinalState() self.camFSM = None FirstPerson.cleanup(self) return