Example #1
0
class world(ShowBase):
    def __init__(self):
        try:
            ShowBase.__init__(self)
        except:
            sys.exit("something went wrong: error while loading OpenGL")

        # ------------------------------- Begin of parameter variables (pretty messy actually) ------------------------------------
        #debug
        self.debug = False  #REMEMBER TO TURN THIS OFF WHEN COMMITTING THIS TO GITHUB YOU GODDAM MORRON !!!
        #debug
        self.dir = Filename.fromOsSpecific(os.getcwd())
        self.timescale = 5
        self.worldscale = 0.1  # currently unused

        self.camera_delta = 0.5  # camera delta displacement
        self.sensitivity_x, self.sensitivity_y = 20, 20
        self.watched = None  # watched object (object focused by the cursor)

        self.state = [
            'paused', 'free', None
        ]  # state of things: [simulation paused/running,camera following object/free,followed object/None]
        print('free mode on')
        self.iteration = 0  #iteration for the menu to be drawn once
        self.collision_status = False  # Keep this on False, that's definitely not a setting # currently unused

        self.u_constant = 6.67408 * 10**(-11)  #just a quick reminder
        self.u_radius = 5.25  #just what I said earlier
        self.u_radius_margin = 0.1  #a margin added to the generic radius as a safety feature (mountains and stuff, atmosphere)

        # ------------------------------- End of parameter variables (sry for the mess) --------------------------------------------

        # Mouse parameters
        self.hidden_mouse = True
        wp = WindowProperties()
        wp.setCursorHidden(self.hidden_mouse)
        self.win.requestProperties(wp)

        # preparing the menu text list:
        self.menu_text = []
        self.menu_text.append(
            self.showsimpletext('The PyOS project V0.10', (0, 0.4),
                                (0.07, 0.07), None, (1, 1, 1, True)))
        self.menu_text.append(
            self.showsimpletext('Resume', (0, 0.3), (0.06, 0.06), None,
                                (1, 1, 1, True)))
        self.menu_text.append(
            self.showsimpletext('Quit', (0, 0.2), (0.06, 0.06), None,
                                (1, 1, 1, True)))

        # btw I found something about energy transmition through thermal radiation. I think it uses some Boltzmann formula stuff. Link here:
        # https://fr.wikibooks.org/wiki/Plan%C3%A9tologie/La_temp%C3%A9rature_de_surface_des_plan%C3%A8tes#Puissance_re%C3%A7ue_par_la_Terre

        # Defining important data lists
        self.sounds = [
            self.loader.loadSfx(self.dir + "/Sound/001.mp3"),
            self.loader.loadSfx(self.dir + "/Sound/002.mp3"),
            self.loader.loadSfx(self.dir + "/Sound/003.mp3"),
            self.loader.loadSfx(self.dir + "/Sound/004.mp3"),
            self.loader.loadSfx(self.dir + "/Sound/005.mp3")
        ]  #buggy
        self.collision_solids = [
        ]  #collision related stuff - comments are useless - just RTFM
        self.light_Mngr = []
        self.data = [
            [
                0, 0, 0, 0, 0.003, 0, 0.15, 0.15, 0.15, 100000.00, True,
                [
                    self.loader.loadModel(self.dir +
                                          "/Engine/lp_planet_0.egg"),
                    (0.1, 0, 0),
                    self.loader.loadModel(self.dir +
                                          "/Engine/lp_planet_1.egg"),
                    (0.14, 0, 0)
                ], "lp_planet", False, 0.1
            ],
            [
                40, 0, 0, 0, 0.003, 0, 0.05, 0.05, 0.05, 20.00, True,
                [
                    self.loader.loadModel(self.dir + "/Engine/Icy.egg"),
                    (0.05, 0, 0)
                ], "Ottilia", False, 0.1
            ],
            [
                0, 70, 10, 0, 0.005, 0, 0.1, 0.1, 0.1, 40.00, True,
                [
                    self.loader.loadModel(self.dir + "/Engine/asteroid_1.egg"),
                    (0, 0, 0.2)
                ], "Selena", False, 1
            ],
            [
                100, 0, 10, 0, 0, 0, 5, 5, 5, 1000000, True,
                [
                    self.loader.loadModel(self.dir + "/Engine/sun1.egg"),
                    (0.01, 0, 0),
                    self.loader.loadModel(self.dir + "/Engine/sun1_atm.egg"),
                    (0.01, 0, 0)
                ], "Sun", True, 0.1
            ],
            [
                -100, 50, 70, 0, 0, 0.002, 0.15, 0.15, 0.15, 1000.00, True,
                [
                    self.loader.loadModel(self.dir + "/Engine/Earth2.egg"),
                    (-0.1, 0, 0),
                    self.loader.loadModel(self.dir + "/Engine/Earth2_atm.egg"),
                    (-0.15, 0, 0)
                ], "Julius_planet", False, 0.1
            ]
            # insert your 3d models here, following the syntax
        ]
        # the correct reading syntax is [x,y,z,l,m,n,scale1,scale2,scale3,mass,static,[file,(H,p,r),file,(H,p,r)...],id,lightsource,brakeforce] for each body - x,y,z: position - l,m,n: speed - scale1,scale2,scale3: obvious (x,y,z) - mass: kg - static: boolean - [files]: panda3d readfiles list - id: str - lightsource: boolean -
        #if you want the hitbox to be correctly scaled, and your body to have reasonable proportions, your 3d model must be a 5*5 sphere, or at least have these proportions

        # create the real data list, the one used by the program
        self.bodies = []

        for c in self.data:
            temp = body()
            temp.fill_entries(c)
            self.bodies.append(temp)
            temp = None
        #self.bodies.reverse()

        # Quick presetting
        self.setBackgroundColor(0, 0, 0, True)

        # non-body type structures loading
        if SKYBOX == 'sky':
            self.isphere = self.loader.loadModel(
                self.dir +
                "/Engine/InvertedSphere.egg")  #loading skybox structure
            self.tex = loader.loadCubeMap(self.dir +
                                          '/Engine/Skybox4/skybox_#.png')
        elif SKYBOX == 'arena':
            self.box = self.loader.loadModel(self.dir + "/Engine/arena.egg")

        #load shaders (optionnal)
        '''
        sun_shader=Shader.load(Shader.SLGLSL,self.dir+'/Engine/Shaders/flare_v.glsl',self.dir+'/Engine/Shaders/flare_f.glsl')
        '''
        self.orbit_lines = []  #under developement

        # see https://www.panda3d.org/manual/?title=Collision_Solids for further collision interaction informations
        base.graphicsEngine.openWindows()
        try:
            # filters predefining
            self.filters = CommonFilters(base.win, base.cam)
            '''
            self.filters.setBlurSharpen(amount=0) # just messing around
            '''
            if not self.debug:
                self.filters.set_gamma_adjust(1.0)  # can be usefull
                self.filters.set_bloom(intensity=1, size="medium")
                render.setAntialias(AntialiasAttrib.MAuto)

            for c in self.bodies:  # loading and displaying the preloaded planets and bodies

                if c.is_lightSource and not self.debug:
                    # VM filtering
                    self.filters.setVolumetricLighting(c.filelist[u],
                                                       numsamples=50,
                                                       density=0.5,
                                                       decay=0.95,
                                                       exposure=0.035)
                    #c.filelist[u].set_shader(sun_shader)
                    if BLUR: self.filters.setCartoonInk()

                for u in range(0, len(c.filelist), 2):  # loading each sub-file
                    c.filelist[u].reparentTo(self.render)
                    c.filelist[u].setScale(tuple(c.scale))
                    c.filelist[u].setPos(tuple(c.position))
                    #setting the collision solid up
                temp = hitbox()
                temp.Volume = CollisionSphere(0, 0, 0, self.u_radius)
                temp.NodePath = c.filelist[0].attachNewNode(CollisionNode(
                    c.id))
                temp.CollisionNode = temp.NodePath.node()
                self.collision_solids.append(
                    temp
                )  #the radius is calculated by using the average scale + the u_radius
                # the structure of the collision_solids list will be: [temp1,temp2,...]
                # asteroids and irregular shapes must be slightly bigger than their hitbox in order to avoid visual glitches
                self.collision_solids[
                    len(self.collision_solids) - 1].CollisionNode.addSolid(
                        self.collision_solids[
                            len(self.collision_solids) -
                            1].Volume)  #I am definitely not explaining that
                temp = None
                if self.debug:
                    loadPrcFileData('', 'show-frame-rate-meter true')
                    self.collision_solids[
                        len(self.collision_solids) -
                        1].NodePath.show()  # debugging purposes only

                print("collision: ok")
                print("placing body: done")
                if c.is_lightSource:
                    self.light_Mngr.append([PointLight(c.id + "_other")])
                    self.light_Mngr[len(self.light_Mngr) - 1].append(
                        render.attachNewNode(
                            self.light_Mngr[len(self.light_Mngr) - 1][0]))
                    self.light_Mngr[len(self.light_Mngr) - 1][1].setPos(
                        tuple(c.position))
                    render.setLight(self.light_Mngr[len(self.light_Mngr) -
                                                    1][1])

                    self.light_Mngr.append([AmbientLight(c.id + "_self")])
                    self.light_Mngr[len(self.light_Mngr) -
                                    1][0].setColorTemperature(3000)
                    self.light_Mngr[len(self.light_Mngr) - 1].append(
                        render.attachNewNode(
                            self.light_Mngr[len(self.light_Mngr) - 1][0]))
                    for u in range(0, len(c.filelist), 2):
                        c.filelist[u].setLight(
                            self.light_Mngr[len(self.light_Mngr) - 1][1])
                    print("lights: done")

                print("loaded new body, out: done")
            if SKYBOX == 'sky':
                self.isphere.setTexGen(
                    TextureStage.getDefault(), TexGenAttrib.MWorldCubeMap
                )  # *takes a deep breath* cubemap stuff !
                self.isphere.setTexProjector(TextureStage.getDefault(), render,
                                             self.isphere)
                self.isphere.setTexPos(TextureStage.getDefault(), 0, 0, 0)
                self.isphere.setTexScale(TextureStage.getDefault(),
                                         .5)  # that's a thing...
                self.isphere.setTexture(
                    self.tex
                )  # Create some 3D texture coordinates on the sphere. For more info on this, check the Panda3D manual.
                self.isphere.setLightOff()
                self.isphere.setScale(10000)  #hope this is enough
                self.isphere.reparentTo(self.render)
            elif SKYBOX == 'arena':
                self.box.setPos(0, 0, 0)
                self.box.setScale(100)
                self.box.reparentTo(self.render)
            # collision traverser and other collision stuff # that's super important, and super tricky to explain so just check the wiki
            self.ctrav = CollisionTraverser()
            self.queue = CollisionHandlerQueue()
            for n in self.collision_solids:
                self.ctrav.add_collider(n.NodePath, self.queue)
            # the traverser will be automatically updated, no need to repeat this every frame
            # debugging only
            if self.debug:
                self.ctrav.showCollisions(render)
            # play a random music
            self.current_playing = random.randint(0, len(self.sounds) - 1)
            self.sounds[self.current_playing].play()

            # task manager stuff comes here
            self.taskMgr.add(self.intro_loop, 'showIntroPic')
        except:
            sys.exit(":( something went wrong: files could not be loaded")
        '''
        self.showsimpletext("All modules loaded, simulation running",(-1.42,0.95),(0.04,0.04),None,(1,1,1,True))
        self.showsimpletext("PyOS build V0.10",(-1.5,0.90),(0.04,0.04),None,(1,1,1,True))
        self.showsimpletext("By l3alr0g",(-1.68,0.85),(0.04,0.04),None,(1,1,1,True))
        '''

        # key bindings
        self.accept('backspace', self.system_break)
        self.accept('escape', self.toggle_pause)
        self.accept('mouse1', self.handle_select, [True])
        self.accept('wheel_up', self.handle_scrolling,
                    [True])  # center button (just a quick test)
        self.accept('wheel_down', self.handle_scrolling, [False])
        self.accept('z', self.move_camera, [0, True])
        self.accept('q', self.move_camera, [1, True])
        self.accept('s', self.move_camera, [2, True])
        self.accept('d', self.move_camera, [3, True])
        self.accept('a', self.move_camera, [4, True])
        self.accept('e', self.move_camera, [5, True])

        self.accept('z-up', self.move_camera, [0, False])
        self.accept('q-up', self.move_camera, [1, False])
        self.accept('s-up', self.move_camera, [2, False])
        self.accept('d-up', self.move_camera, [3, False])
        self.accept('a-up', self.move_camera, [4, False])
        self.accept('e-up', self.move_camera, [5, False])
        self.keymap = [
            'z', 0, 'q', 0, 's', 0, 'd', 0, 'a', 0, 'e', 0, 'mouse1', 0
        ]

        self.disable_mouse()

        if self.debug:
            # draw axis
            coord = [(1, 0, 0), (0, 1, 0), (0, 0, 1)]
            axis = []
            for c in range(3):
                axis.append(LineSegs())
                axis[c].moveTo(0, 0, 0)
                axis[c].drawTo(coord[c])
                axis[c].setThickness(3)
                axis[c].setColor(
                    tuple([coord[c][u] * 255
                           for u in range(len(coord[c]))] + [True]))
                NodePath(axis[c].create()).reparent_to(render)

        # camera positionning -------
        self.focus_point = [
            0, 0, 0
        ]  # point focused: can become a body's coordinates if the user tells the program to do so
        self.zoom_distance = 30  # distance to the focus point in common 3D units (can be modified by scrolling)
        self.cam_Hpr = [0, 0, 0]  # phi, alpha, theta - aka yaw, pitch, roll
        self.cam_Hpr = [
            self.cam_Hpr[n] * pi / 180 for n in range(len(self.cam_Hpr))
        ]  # convert to rad
        phi, alpha, theta, zoom, object = self.cam_Hpr[
            0] * pi / 180, self.cam_Hpr[1] * pi / 180, self.cam_Hpr[
                2] * pi / 180, self.zoom_distance, self.state[
                    2]  # temporary vars
        if self.state[1] == 'free':
            self.camera_pos = [0, 0, 0]
            self.camera.setPos(tuple(self.camera_pos))
        elif self.state[1] == 'linked':
            # find the object (self.state[2]) in the data list
            list_pos = [
                self.bodies[n].filelist[0] for n in range(len(self.bodies))
            ].index(object.getParent())
            self.focus_point = self.bodies[
                list_pos].position  # take the focused object's coordinates
            self.camera_pos = [
                self.focus_point[0] + sin(phi) * cos(-alpha) * zoom,
                self.focus_point[1] - cos(phi) * cos(-alpha) * zoom,
                self.focus_point[2] + sin(-alpha) * zoom
            ]  #keep it up to date so that it's not hard to find whend switching modes
            self.camera.setPos(tuple(self.camera_pos))
            self.camera.setHpr(self.cam_Hpr)

        # cursor
        self.cursor = self.showsimpletext('.', (0, 0), (0.08, 0.08), None, (
            1, 1, 1,
            True))  # yeah, you can laugh, but this still works so I don't care
        self.pointerNode = CollisionNode('cursor')
        self.pointerNP = camera.attachNewNode(self.pointerNode)
        self.pointerNode.setFromCollideMask(
            BitMask32.bit(1)
        )  # separate collisions (in order to avoid mistakes during physical calculations)
        self.cursor_ray = CollisionRay()  # create the mouse control ray
        self.pointerNode.addSolid(self.cursor_ray)
        self.ctrav.add_collider(self.pointerNP, self.queue)

        return None

    def showsimpletext(
        self, content, pos, scale, bg, fg
    ):  #shows a predefined, basic text on the screen (variable output only)
        return OnscreenText(text=content, pos=pos, scale=scale, bg=bg, fg=fg)

    def intro_loop(self, task):
        if not (task.time):
            self.screen_fill = OnscreenImage(image=str(self.dir) +
                                             "/Engine/main_page.png",
                                             pos=(0, 0, 0),
                                             scale=(1.77777778, 1, 1))
        elif task.time > 3:
            self.screen_fill.destroy()
            self.taskMgr.add(self.mouse_check, 'mousePositionTask')
            self.taskMgr.add(self.placement_Mngr, 'frameUpdateTask')
            self.taskMgr.add(self.Sound_Mngr, 'MusicHandle')
            self.taskMgr.add(self.camera_update, 'cameraPosition')
            self.taskMgr.remove('showIntroPic')
            return None
        return task.cont

    def placement_Mngr(
        self, task
    ):  # main game mechanics, frame updating function (kinda, all pausing and menu functions must be applied here
        if self.state[0] == 'running' or not task.time:
            self.ctrav.traverse(render)
            #self.queue = CollisionHandlerQueue() # update the collision queue
            brakeforce = [0 for n in range(len(self.bodies))
                          ]  # create an empty brakeforce list
            if self.queue.getNumEntries():
                if self.debug:
                    print(self.queue.getNumEntries())  # debug
                # now we have to create a temp list containing only the Entries that refer to collisions between bodies,
                # not cursor-type collisions:
                temp1, temp2 = [], []
                for count in range(len(self.queue.getEntries())):
                    if self.queue.getEntries()[count].getFromNodePath(
                    ) != self.pointerNP:
                        temp1.append(self.queue.getEntries()[count])
                    else:
                        temp2.append(self.queue.getEntries()[count])
                # the temp1 and temp2 lists have been created
                # run the check for the body-with-body collisions
                for c in range(0, len(temp1), 2):
                    entry = temp1[c]
                    brakeforce = self.collision_log(entry, brakeforce)
                # run the check for the cursor-with-body collisions
                for c in range(len(temp2)):
                    entry = temp2[c]
                    self.watched = entry.getIntoNodePath()
                # print "out"

                # update the collider list
                self.ctrav.clear_colliders()
                self.queue = CollisionHandlerQueue()
                for n in self.collision_solids:
                    self.ctrav.add_collider(n.NodePath, self.queue)
                self.ctrav.add_collider(self.pointerNP,
                                        self.queue)  # add the cursor ray again
            else:
                self.watched = None

            # collision events are now under constant surveillance
            acceleration = []
            for c in range(len(self.bodies)):  #selects the analysed body
                var = self.bodies[c]
                Bdf = [
                    0, 0, 0
                ]  #Bdf stands for 'bilan des forces' in french, it's the resulting acceleration
                for d in self.bodies[0:c] + self.bodies[c + 1:len(
                        self.bodies
                ) - 1]:  #selects the body which action on the analysed body we're studying...not sure about that english sentence though
                    S, M = [d.mass] + d.position, [var.mass] + var.position
                    temp = self.dual_a(S, M)
                    Bdf = [Bdf[x] + temp[x] for x in range(3)]  # list sum
                # add the result to the global save list
                acceleration.append(Bdf)
            #update the bodies' position
            self.speed_update(acceleration, brakeforce)
            self.pos_update()
            self.apply_update()
        elif self.state[0] == 'paused':
            self.handle_menu(self.iteration)
            self.iteration += 1
        return task.cont

    def speed_update(self, a, brakeforce):
        for c in range(len(self.bodies)
                       ):  #the function updates the speed tuple accordingly
            self.bodies[c].speed[0] += self.timescale * a[c][0]
            #self.bodies[c].speed[0]/=brakeforce[c]+1 # aero/lytho braking has to be applied to the colliding object
            # actually, speed isn't applied that way
            self.bodies[c].speed[1] += self.timescale * a[c][1]
            #self.bodies[c].speed[1]/=brakeforce[c]+1
            self.bodies[c].speed[2] += self.timescale * a[c][2]
            #self.bodies[c].speed[2]/=brakeforce[c]+1
        return 0

    def pos_update(self):  #updates the positional coordinates
        for c in range(len(self.bodies)):
            self.bodies[c].position[
                0] += self.timescale * self.bodies[c].speed[0]
            self.bodies[c].position[
                1] += self.timescale * self.bodies[c].speed[1]
            self.bodies[c].position[
                2] += self.timescale * self.bodies[c].speed[2]
        return 0

    def apply_update(self):  #actually moves the hole 3d stuff around
        count = 0  #local counter
        for c in self.bodies:
            for u in range(len(c.filelist)):
                if u % 2 != 0:
                    c.filelist[u - 1].setHpr(c.filelist[u - 1], c.filelist[u])
                else:
                    c.filelist[u].setPos(tuple(c.position))
            if c.is_lightSource:
                self.light_Mngr[count][1].setPos(tuple(c.position))
                count += 2  #we have to change the position of the pointlight, not the ambientlight
        return 0

    def camera_update(self, task):
        phi, alpha, theta, zoom, object = self.cam_Hpr[
            0] * pi / 180, self.cam_Hpr[1] * pi / 180, self.cam_Hpr[
                2] * pi / 180, self.zoom_distance, self.state[2]
        if self.state[1] == 'free':
            self.camera.setPos(tuple(self.camera_pos))
        elif self.state[1] == 'linked':
            # find the object (self.state[2]) in the data list
            list_pos = [
                self.bodies[n].filelist[0] for n in range(len(self.bodies))
            ].index(object.getParent())
            self.focus_point = self.bodies[
                list_pos].position  # take the focused object's coordinates
            self.camera_pos = [
                self.focus_point[0] + sin(phi) * cos(-alpha) * zoom,
                self.focus_point[1] - cos(phi) * cos(-alpha) * zoom,
                self.focus_point[2] + sin(-alpha) * zoom
            ]
            self.camera.setPos(tuple(self.camera_pos))
            self.camera.setHpr(tuple(self.cam_Hpr))
        ''' # not finished yet
        self.camera.setPos(self.focus_point[0]+cos(self.cam_Hpr[0])*self.zoom_distance,self.focus_point[1]+sin(self.cam_Hpr[0])*self.zoom_distance,self.focus_point[2]+sin(self.cam_Hpr[1])*self.zoom_distance)
        self.camera.lookAt(self.focus_point[0],self.focus_point[1],self.focus_point[2])
        '''
        # collision cursor stuff goes here:
        self.cursor_ray.setFromLens(self.camNode, 0, 0)
        # relatively to the camera, the cursor position will always be 0,0 which is the position of the
        # white point on the screen

        if self.keymap != ['z', 0, 'q', 0, 's', 0, 'd', 0]:
            for x in range(1, len(self.keymap), 2):
                if self.keymap[x]:
                    self.move_camera(
                        int((x - 1) / 2), True
                    )  # why (x-1)/2 ? because we have to make the tow readable as a key number, like 0,1,2,3
        return task.cont

    def dual_a(
        self, S, M
    ):  #S is the "static object", the one that applies the force to the "moving" object M
        O = []  #This will be the list with the accelerations for an object
        d = sqrt((S[1] - M[1])**2 + (S[2] - M[2])**2 + (S[3] - M[3])**2)
        x = (self.u_constant * S[0] * (S[1] - M[1])) / d**2
        y = (self.u_constant * S[0] * (S[2] - M[2])) / d**2
        z = (self.u_constant * S[0] * (S[3] - M[3])) / d**2
        O.append(x)
        O.append(y)
        O.append(z)
        return O

    def collision_log(self, entry, brakeforce):
        from_pos = [
            self.bodies[n].filelist[0] for n in range(len(self.bodies))
        ].index(entry.getFromNodePath().getParent())
        into_pos = [
            self.bodies[n].filelist[0] for n in range(len(self.bodies))
        ].index(entry.getIntoNodePath().getParent()
                )  #find the nodepath in the list
        f_radius = sum(self.bodies[from_pos].scale) * self.u_radius / 3
        i_radius = sum(self.bodies[into_pos].scale) * self.u_radius / 3
        if max(f_radius, i_radius) == f_radius:
            inverted = True
            into_pos, from_pos = from_pos, into_pos
        else:
            inverted = False  # currently unused
        brakeforce[from_pos] = self.bodies[
            from_pos].brakeforce  # get the force given in the data list
        # those are the two positions of the nodepaths, now we need to know which one is bigger, in order to obtain the fusion effect
        # from_pos is the smaller body, into_pos is the bigger one
        self.collision_gfx(
            self.momentum_transfer(from_pos, into_pos, entry, inverted),
            f_radius, i_radius)
        return brakeforce

    def momentum_transfer(self, f_pos, i_pos, entry, inverted):
        if self.debug:
            print("colliding")  # debug, makes the game laggy
        interior = entry.getInteriorPoint(entry.getIntoNodePath())  # default
        surface = entry.getSurfacePoint(entry.getIntoNodePath())
        print((interior - surface).length())  # debug
        if (interior - surface).length() >= 2 * sum(
                self.bodies[f_pos].scale) * self.u_radius / 3:
            if self.state[2] == self.collision_solids[f_pos].NodePath:
                self.state[1] = 'free'
                self.state[2] = None
            self.ctrav.remove_collider(self.collision_solids[f_pos].NodePath)
            self.bodies[f_pos].delete_body()

            self.bodies[i_pos].scale[0] *= (
                self.bodies[i_pos].mass +
                self.bodies[f_pos].mass) / self.bodies[i_pos].mass
            self.bodies[i_pos].scale[1] *= (
                self.bodies[i_pos].mass +
                self.bodies[f_pos].mass) / self.bodies[i_pos].mass
            self.bodies[i_pos].scale[2] *= (
                self.bodies[i_pos].mass +
                self.bodies[f_pos].mass) / self.bodies[i_pos].mass
            self.bodies[i_pos].mass += self.bodies[f_pos].mass
            # scale updating ()
            ''' temporarly removed
            for c in range(0,len(self.bodies[i_pos].filelist),2):
                self.bodies[i_pos].filelist[c].setScale(tuple(self.bodies[i_pos].scale))
            '''
            # deleting the destroyed planet's data
            self.bodies = self.bodies[:f_pos] + self.bodies[f_pos +
                                                            1:len(self.bodies)]
            self.collision_solids = self.collision_solids[:f_pos] + self.collision_solids[
                f_pos + 1:len(self.collision_solids)]
            # just a quick test
            if self.debug:
                self.ctrav.showCollisions(render)
            if self.debug:
                print("planet destroyed")
        return interior, surface  # used for the collision gfx calculations

    def printScene(self):  #debug
        file = open("scenegraph.txt", "a")
        ls = LineStream()
        render.ls(ls)
        while ls.isTextAvailable():
            file.write(ls.getLine())
            file.write("\n")
        file.write("\n")
        file.write("END\n")
        file.write("\n")
        file.close()

    def Sound_Mngr(self, task):
        if self.sounds[self.current_playing].length() - self.sounds[
                self.current_playing].getTime(
                ) == 0:  #could have just used not()
            self.current_playing = random.choice(
                list(range(0, self.current_playing)) +
                list(range(self.current_playing + 1, len(self.sounds))))
            self.sounds[self.current_playing].play()
        return task.cont

    def collision_gfx(self, points, Rf,
                      Ri):  # collision animation calculations
        # section size calculation
        # we know the depth of penetration (no silly jokes please), which allows us, knowing the radius of each body,
        # to calculate the radius of the section (I've got no idea how to say that in correct english)
        # the display of the particles all over this circle will be a piece of cake (at least I hope so)
        # see documents in the screenshot folder for more informations about the maths
        interior, surface = points[0], points[1]
        p = (interior - surface).length()
        p2 = (p**2 - 2 * Ri * p) / (2 * Ri - 2 * p - 2 * Rf)
        p1 = p - p2
        # now we know everything about our impact section (the circle that defines the contact between the two bodies)
        # we just have to find the coord of the circle's center

        return 0

    def create_crater(self):  # see project for more informations
        return None

    def toggle_pause(self):
        temp = ['paused', 'running']
        self.state[0] = temp[self.state[0] ==
                             temp[0]]  # switches between paused and running
        self.iteration = 0
        if self.state[0] == 'paused':
            self.handle_menu(self.iteration)
        else:
            self.filters.del_blur_sharpen()
            # make the mouse invisible
            self.hidden_mouse = True
            wp = WindowProperties()
            wp.setCursorHidden(self.hidden_mouse)
            # set the mouse pos to 0
            self.center_mouse()

            self.win.requestProperties(wp)
            for u in self.menu_text:
                u.hide()
        return None

    def handle_menu(self, iteration):
        if not iteration:
            self.accept('escape', self.toggle_pause)
            self.draw_menu()
            # make the mouse visible
            self.hidden_mouse = False
            wp = WindowProperties()
            wp.setCursorHidden(self.hidden_mouse)
            self.win.requestProperties(wp)
        else:
            a = 1  # indentation (temporary)
            #put your mouse detection stuff here
        return None

    def draw_menu(self):
        self.filters.setBlurSharpen(amount=0)
        for u in self.menu_text:
            u.show()
        return None

    def show_credits(self):
        print(
            "created by l3alr0g (at least this part, I'll do something better at the end)"
        )
        return None

    def system_break(self):
        # place your data saving routines here
        print("system exit successful, data saved")
        print("executing sys.exit()")
        print("out: done")
        sys.exit(0)
        return None

    def handle_scrolling(
            self,
            up):  # up is a boolean: up=True means up, up=False means down
        if up and self.state[0] == 'running':
            if self.state[1] == 'linked':
                self.zoom_distance *= 0.95
            else:
                self.camera_delta *= 1.1
        elif not up and self.state[0] == 'running':
            if self.state[1] == 'linked':
                self.zoom_distance /= 0.95
            else:
                self.camera_delta /= 1.1
        return None

    def rotate_camera(self):
        self.camera.setHpr(tuple(self.cam_Hpr))
        return None

    def move_camera(
        self, tow, pressed
    ):  # tow stands for towards, pressed is a boolean which indicates the state of the key
        if pressed:
            self.keymap[2 * tow + 1] = 1
            self.state[1] = 'free'
            #print('free mode on')
            self.state[2] = None
        else:
            self.keymap[2 * tow + 1] = 0

        if self.keymap[2 * tow + 1]:
            phi, alpha, theta, delta, zoom = self.cam_Hpr[
                0] * pi / 180, self.cam_Hpr[1] * pi / 180, self.cam_Hpr[
                    2] * pi / 180, self.camera_delta, self.zoom_distance
            if self.keymap[2 * tow] == 'q':
                if self.state[1] == 'free':
                    self.camera_pos = [
                        self.camera_pos[0] - cos(phi) * cos(theta) * delta,
                        self.camera_pos[1] - sin(phi) * cos(theta) * delta,
                        self.camera_pos[2] + sin(theta) * delta
                    ]  # moving the camera
            if self.keymap[2 * tow] == 'z':
                if self.state[1] == 'free':
                    self.camera_pos = [
                        self.camera_pos[0] - sin(phi) * cos(alpha) * delta,
                        self.camera_pos[1] + cos(phi) * cos(alpha) * delta,
                        self.camera_pos[2] + sin(alpha) * delta
                    ]
            if self.keymap[2 * tow] == 's':
                if self.state[1] == 'free':
                    self.camera_pos = [
                        self.camera_pos[0] + sin(phi) * cos(alpha) * delta,
                        self.camera_pos[1] - cos(phi) * cos(alpha) * delta,
                        self.camera_pos[2] - sin(alpha) * delta
                    ]
            if self.keymap[2 * tow] == 'd':
                if self.state[1] == 'free':
                    self.camera_pos = [
                        self.camera_pos[0] + cos(phi) * cos(theta) * delta,
                        self.camera_pos[1] + sin(phi) * cos(theta) * delta,
                        self.camera_pos[2] - sin(theta) * delta
                    ]
            if self.keymap[2 * tow] == 'a':
                self.cam_Hpr[2] -= 1
            if self.keymap[2 * tow] == 'e':
                self.cam_Hpr[2] += 1
        return None

    def mouse_check(self, task):  # gets the mouse's coordinates
        mwn = self.mouseWatcherNode
        if mwn.hasMouse():
            x, y = mwn.getMouseX(), mwn.getMouseY()
            #print(x,y) # debug
            # focus_point coordinates modifier code here:
            if self.state == ['running', 'free', None]:
                self.cam_Hpr[
                    0] -= x * self.sensitivity_x  # the - fixes a bug I can't solve
                self.cam_Hpr[
                    1] += y * self.sensitivity_y  # those formulas do not work when theta (self.cam_Hpr[2]) changes
                self.rotate_camera()
                self.center_mouse()
            elif self.state[0] == 'running' and self.state[1] == 'linked':
                self.cam_Hpr[0] -= x * self.sensitivity_x
                self.cam_Hpr[1] -= y * self.sensitivity_y
                self.rotate_camera()
                self.center_mouse()
            '''
            if self.debug:
                print(self.cam_Hpr,self.camera_pos) # debug
        '''
        return task.cont

    def center_mouse(self):
        self.win.movePointer(
            0, int(self.win.getProperties().getXSize() / 2),
            int(self.win.getProperties().getYSize() / 2)
        )  # move mouse back to center --> careful ! this makes the delta calculation code buggy

    def handle_select(self, is_clicked):
        if is_clicked and self.watched != None:
            self.state[1] = 'linked'  # toggle following mode
            self.state[2] = self.watched
            print('linked mode on, focusing: ', self.watched)
        #else: # do nothing actually
        return None

    def easter_egg(self):
        return "please be patient, our hens are working on it"
Example #2
0
 def volumetricLightTest(self, model):
     fltr = CommonFilters(base.win, base.cam)
     fltr.setVolumetricLighting(model, 64, 2, .85, 0.3)
Example #3
0
	def volumetricLightTest(self, model):
		fltr = CommonFilters(base.win, base.cam) 
		fltr.setVolumetricLighting(model,64,2,.85,0.3)
Example #4
0
class world(ShowBase):
    def __init__(self):
        try:
            ShowBase.__init__(self)
        except:
            sys.exit("[WARNING]: unknown error: Showbase initialisation failed")
        
        
        # ------------------------------- Begin of parameter variables (pretty messy actually) ------------------------------------
        #debug ------
        self.debug=False #[ /!\ IMPORTANT /!\ ] do not leave this value to True when you commit the code to github, as it is impossible to change once ingame, and modifies quite a lot of graphical parameters 
        #debug ------

        self.stored_collisions=[] # this counts the amount of collisions and allows us to detect the income of a new collision in order to create a new particle effect when it appears
        
        self.timescale=5 # this can be changed at any moment, it represents the speed of the ingame time
        self.worldscale=0.1 # currently useless
        
        self.camera_delta=0.5 # camera delta displacement
        self.sensitivity_x,self.sensitivity_y=20,20
        self.watched=None # watched object (object focused by the cursor)
        
        self.state=['paused','free',None] # state of things: [simulation paused/running,camera following object/free,followed object/None]
        print('free mode on')
        self.iteration=0 #iteration for the menu to be drawn once
        self.collision_status=False # Keep this on False, that's definitely not a setting # currently useless

        self.u_constant=6.67408*10**(-11) #just a quick reminder
        self.u_radius=5 #just what I said earlier 
        
        # ------------------------------- End of parameter variables (sry for the mess) --------------------------------------------
        

        self.Game_state=state()
        self.Game_state.cleanup() # better safe than sorry
        self.taskMgr.add(self.wait,'wait_task')
        self.setBackgroundColor(0,0,0)
        
    def wait(self,task):
        if not(task.time):
            self.screen_fill=OnscreenImage(image=str(MAINDIR)+"/Engine/main_page.png",pos = (0, 0, 0),scale=(1.77777778,1,1))
        elif task.time>4:
            self.taskMgr.remove('wait_task')
            self.screen_fill.destroy()
            self.menu()
            return None
        return task.cont


    def menu(self):
        # loading time
        # music
        self.menu_music=self.loader.loadSfx(str(MAINDIR)+'/Sound/deadmau5-cabin.mp3')
        self.menu_music.setLoop(True)
        self.menu_music.play()
        # filters
        try:
            self.filters = CommonFilters(self.win, self.cam)
        except: pass
        self.Game_state.root_node.setAntialias(AntialiasAttrib.MAuto)
        def quit():
            sys.exit(0)
        

        button_block_pos=(-1.2,0.35,0.25)
        maps_start=loader.loadModel(str(MAINDIR)+'/Engine/start.egg')
        maps_quit=loader.loadModel(str(MAINDIR)+'/Engine/quit.egg')
        maps_set=loader.loadModel(str(MAINDIR)+'/Engine/set.egg')
        self.start_button=DirectButton(pos=button_block_pos,frameColor=(0,0,0,0),scale=(0.717,0.4,0.221),geom=(maps_start.find('**/Start'),maps_start.find('**/Start_push'),maps_start.find('**/Start_on'),maps_start.find('**/Start')),command=self.loadgame)
        self.quit_button=DirectButton(pos=(button_block_pos[0]+0.135,button_block_pos[1],button_block_pos[2]-0.4),frameColor=(0,0,0,0),scale=(1,0.4,0.221),geom=(maps_quit.find('**/Quit'),maps_quit.find('**/Quit_push'),maps_quit.find('**/Quit_on'),maps_quit.find('**/Quit')),command=quit)
        self.settings_button=DirectButton(pos=(button_block_pos[0]-0.075,button_block_pos[1],button_block_pos[2]-0.2),frameColor=(0,0,0,0),scale=(0.56,0.4,0.221),geom=(maps_set.find('**/Set'),maps_set.find('**/Set_push'),maps_set.find('**/Set_on'),maps_set.find('**/Set')),command=self.not_implemented_yet) # settings not implemented yet
        self.title_pic=OnscreenImage(image=str(MAINDIR)+'/Engine/title.png',pos=(0,0.35,0), scale=(0.51,1,0.5))
        self.title_pic.setTransparency(TransparencyAttrib.MAlpha)
        
        pannel_pos_x=1.25
        self.activity_log=OnscreenImage(image=str(MAINDIR)+'/Engine/activity_log.png',pos=(pannel_pos_x,0.35,0.30),scale=(0.375,0.75,0.086775))
        self.activity_log.setTransparency(TransparencyAttrib.MAlpha)
        #self.activity_log_bg=OnscreenImage(image=str(MAINDIR)+'/Engine/activity_log_bg.png',pos=(pannel_pos_x,-0.35,-0.3),scale=(0.5,0.4,0.675))
        #self.activity_log_bg.setTransparency(TransparencyAttrib.MAlpha)
        #spaces compensate the center text effect
        self.logs=OnscreenText(text='   PyOS v0.11       \n\n                              Added main menu in last update\n                                             Particle support has now become reality\n                                  but still needs some improvement\n\n\n          Feature in progress:\n      collision animation\n\n\nRelease date >>',pos=(pannel_pos_x-0.3,0.11,0.2), scale=(0.05,0.05,0.05),fg=(1,1,1,1))
        self.shrug=OnscreenImage(image=str(MAINDIR)+'/Engine/shrug.png',pos=(pannel_pos_x-0.35,0.35,-0.48),scale=(0.1,1,0.0317))
        self.shrug.setTransparency(TransparencyAttrib.MAlpha)

        self.backgrnd=OnscreenImage(image=str(MAINDIR)+'/Engine/Stars.png',scale=(85.44,1,48),pos=(0,60,0))
        self.backgrnd.reparentTo(self.Game_state.root_node)
        

        #self.filters.set_gamma_adjust(0.9)
        self.moon=self.loader.loadModel(str(MAINDIR)+"/Engine/Icy.egg")
        self.moon.setScale(0.2,0.2,0.2)
        self.moon.setPos(0,50,0) # radius of orbit equals 50 units
        self.moon.reparentTo(self.Game_state.root_node)
        self.intro_planet=self.loader.loadModel(str(MAINDIR)+"/Engine/tessena.egg")
        self.intro_planet_atm=self.loader.loadModel(str(MAINDIR)+"/Engine/tessena_atm.egg")
        self.intro_planet.setPos(0,-35,0)
        self.intro_planet_atm.setPos(0,-35,0)
        self.intro_planet.reparentTo(self.Game_state.root_node)
        self.intro_planet_atm.reparentTo(self.Game_state.root_node)
        self.intro_planet.setHpr(-110,0,0)
        self.intro_planet_atm.setHpr(-110,0,0)
        self.cam.setPos(0,-70,0)

        self.disable_mouse()
        # lighting
        d=DirectionalLight('menu_dlight')
        d.setColor(VBase4(0.631,0.369,1,1))
        dlight=self.Game_state.root_node.attachNewNode(d)
        dlight.setHpr(60,-30,0)

        e=DirectionalLight('menu_dlight2')
        e.setColor(VBase4(1,1,1,1))
        elight=self.Game_state.root_node.attachNewNode(e)
        elight.setHpr(60,-30,0)

        self.moon.setLight(elight)
        self.intro_planet.setLight(dlight)
        self.intro_planet_atm.setLight(dlight)

        self.task_mgr.add(self.rotate,'rotationtask') # penser a l'enlever

        
    
    def rotate(self,task):
        self.intro_planet.setHpr(self.intro_planet,(0.1,0,0))
        self.intro_planet_atm.setHpr(self.intro_planet_atm,(0.07,0,0))
        self.moon.setHpr(self.moon,(0.2,0,0))
        self.moon.setPos(50*sin(task.time*0.02),50*cos(task.time*0.02),0)
        return task.cont
    
    # end of menu subfunctions


    def loadgame(self):
        # transition phase
        self.menu_music.setLoop(False)
        self.menu_music.stop()
        
        
        self.taskMgr.remove('rotationtask')
        self.cam.setPos(0,0,0)
        self.Game_state.cleanup()
        self.activity_log.hide()
        #self.activity_log_bg.hide()
        self.logs.hide()
        self.shrug.hide()
        self.start_button.hide()
        self.quit_button.hide()
        self.settings_button.hide()
        self.title_pic.hide()
        
        

        # end of transition phase

        # Mouse parameters 
        self.hidden_mouse=True
        wp = WindowProperties()
        wp.setCursorHidden(self.hidden_mouse)
        self.win.requestProperties(wp)

        # preparing the menu text list:
        quit_to_desk=self.loader.loadModel(str(MAINDIR)+"/Engine/quit_to_desktop.egg")
        quit_to_menu=self.loader.loadModel(str(MAINDIR)+"/Engine/quit_to_menu.egg")
        resume=self.loader.loadModel(str(MAINDIR)+"/Engine/resume.egg")
        self.paused_menu_text=[]
        global_tempPosx=-1
        self.paused_menu_text.append(DirectButton(pos=(global_tempPosx-0.065,0,-0.2),frameColor=(0,0,0,0),scale=(1,0.4,0.211),geom=(quit_to_desk.find('**/quit_to_desktop'),quit_to_desk.find('**/quit_to_desktop_push'),quit_to_desk.find('**/quit_to_desktop_on'),quit_to_desk.find('**/quit_to_desktop')),command=self.system_break))
        self.paused_menu_text.append(DirectButton(pos=(global_tempPosx,0,0),frameColor=(0,0,0,0),scale=(1.12,0.4,0.211),geom=(quit_to_menu.find('**/quit_to_menu'),quit_to_menu.find('**/quit_to_menu_push'),quit_to_menu.find('**/quit_to_menu_on'),quit_to_menu.find('**/quit_to_menu')),command=self.ingame_back_to_menu))
        self.paused_menu_text.append(DirectButton(pos=(global_tempPosx-0.325,0,0.2),frameColor=(0,0,0,0),scale=(0.47,0.4,0.211),geom=(resume.find('**/resume'),resume.find('**/resume_push'),resume.find('**/resume_on'),resume.find('**/resume')),command=self.toggle_pause))

        # btw I found something about energy transmission through thermal radiation. I think it uses some Boltzmann formula stuff. Link here:
        # https://fr.wikibooks.org/wiki/Plan%C3%A9tologie/La_temp%C3%A9rature_de_surface_des_plan%C3%A8tes#Puissance_re%C3%A7ue_par_la_Terre

        # Defining important data lists
        # music imports (paths)
        self.sounds=[str(MAINDIR)+"/Sound/001.mp3",
        str(MAINDIR)+"/Sound/Blazing-Stars.mp3",
        str(MAINDIR)+"/Sound/Cold-Moon.mp3",
        str(MAINDIR)+"/Sound/Light-Years_v001.mp3",
        str(MAINDIR)+"/Sound/The-Darkness-Below.mp3",
        str(MAINDIR)+"/Sound/Retro-Sci-Fi-Planet.mp3",
        str(MAINDIR)+"/Sound/droid-bishop-nightland.mp3",
        str(MAINDIR)+"/Sound/interstellar-ost-03-dust-by-hans-zimmer.mp3",
        str(MAINDIR)+"/Sound/interstellar-ost-04-day-one-by-hans-zimmer.mp3",
        str(MAINDIR)+"/Sound/ascendant-remains-2015.mp3",
        str(MAINDIR)+"/Sound/droid-bishop-nightland.mp3",
        str(MAINDIR)+"/Sound/john-carpenter-utopian-facade-official-music-video.mp3",
        str(MAINDIR)+"/Sound/stranger-things-2-eulogy.mp3",
        str(MAINDIR)+"/Sound/interstellar-ost-07-the-wormhole-by-hans-zimmer.mp3"] 
        
        self.collision_solids=[] #collision related stuff - comments are useless - just RTFM
        self.light_Mngr=[]
        self.data=[
        [0,0,0,-0.00,0.003,0,0.30,0.30,0.30,100000.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/lp_planet_0.egg"),(0.1,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/lp_planet_1.egg"),(0.14,0,0)],"low_poly_planet01",False,0.1]
        ,[10,0,0,0,0.003,0,0.05,0.05,0.05,20.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/Icy.egg"),(0.05,0,0)],"Ottilia_modified",False,0.1]
        ,[0,70,10,0,0.005,0,0.1,0.1,0.1,40.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/asteroid_1.egg"),(0,0,0.2)],"Selena",False,1]
        ,[100,0,10,0,0,0,5,5,5,1000000,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/sun1.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/sun1_atm.egg"),(0.01,0,0)],"Sun",True,0.1]
        ,[-100,50,70,0,0,0.003,0.15,0.15,0.15,1000.00,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/Earth2.egg"),(-0.1,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/Earth2_atm.egg"),(-0.15,0,0)],"big_fucking_planet",False,0.1]
        ,[200,0,0,-0.001,0,0.01,0.1,0.1,0.1,100000,False,[self.loader.loadModel(MAINDIR+"/Engine/realistic_asteroid.egg"),(0,0.01,0)],"spaceship",False,0]
        ,[0,-120,0,0.004,0,0,0.3,0.3,0.3,100000,True,[self.loader.loadModel(str(MAINDIR)+"/Engine/FG1.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG2.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG3.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG4.egg"),(0.01,0,0),self.loader.loadModel(str(MAINDIR)+"/Engine/FG5.egg"),(0.01,0,0)],"Frozen_giant",False,0.1]
        # insert your 3d models here, following the syntax (this is the default scene that will be loaded on startup)
        ] 
        # the correct reading syntax is [x,y,z,l,m,n,scale1,scale2,scale3,mass,static,[file,(H,p,r),file,(H,p,r)...],id,lightsource,brakeforce] for each body - x,y,z: position - l,m,n: speed - scale1,scale2,scale3: obvious (x,y,z) - mass: kg - static: boolean - [files]: panda3d readfiles list (first file must be the ground, the others are atmosphere models)
        #id: str - lightsource: boolean -
        #if you want the hitbox to be correctly scaled,and your body to have reasonable proportions, your 3d model must be a 5*5 sphere, or at least have these proportions
        
        # create the real data list, the one used by the program
        self.bodies=[]
        
        for c in self.data:
            temp=body()
            temp.fill_entries(c)
            self.bodies.append(temp)
            temp=None
        #self.bodies.reverse()
        
        # Quick presetting
        self.setBackgroundColor(0,0,0,True)
        
        # enable particles
        
        self.enableParticles()
        self.toggle_particles() # debug
        # create particle class object
        self.particle=particle()
        # intialize object:
        self.particle.config_path='/Engine/destruction_sphere.ptf' # the MAINDIR is already included inside the class definition

        # non-body type structures loading
        if SKYBOX=='sky':
            self.isphere=self.loader.loadModel(str(MAINDIR)+"/Engine/InvertedSphere.egg") #loading skybox structure
            self.tex=loader.loadCubeMap(str(MAINDIR)+'/Engine/Skybox4/skybox_#.png')
        elif SKYBOX=='arena':
            self.box=self.loader.loadModel(str(MAINDIR)+"/Engine/arena.egg") 
        
        #load shaders (optionnal)
        '''
        sun_shader=Shader.load(Shader.SLGLSL,MAINDIR+'/Engine/Shaders/flare_v.glsl',MAINDIR+'/Engine/Shaders/flare_f.glsl')
        '''
        self.camLens.setNearFar(0.5, 100000)
        
        self.orbit_lines=[] #under developement
        
        # see https://www.panda3d.org/manual/?title=Collision_Solids for further collision interaction informations
        base.graphicsEngine.openWindows()
        try:
            print('\n[Loader manager]:\n')
            '''
            self.filters.setBlurSharpen(amount=0) # just messing around
            '''
            if not self.debug:
                self.filters.set_gamma_adjust(1.0) # can be usefull
                self.filters.set_bloom(intensity=1,size="medium")
            

            for c in self.bodies: # loading and displaying the preloaded planets and bodies
                
                if c.is_lightSource and not self.debug:
                    # VM filtering
                    self.filters.setVolumetricLighting(c.filelist[0],numsamples=50,density=0.5,decay=0.95,exposure=0.035) 
                    #c.filelist[u].set_shader(sun_shader)
                    if BLUR: self.filters.setCartoonInk()
                
                for u in range(0,len(c.filelist),2): # loading each sub-file
                    c.filelist[u].reparentTo(self.Game_state.root_node)
                    c.filelist[u].setScale(tuple(c.scale))
                    c.filelist[u].setPos(tuple(c.position))
                    if u==0 and not(c.is_lightSource):
                        c.filelist[u].setShaderAuto() #activate auto shading for compact, non translucent bodies
                    #setting the collision solid up
                temp=hitbox()
                temp.Volume=CollisionSphere(0,0,0,self.u_radius)
                temp.NodePath=c.filelist[0].attachNewNode(CollisionNode(c.id))
                temp.CollisionNode=temp.NodePath.node()
                self.collision_solids.append(temp) #the radius is calculated by using the average scale + the self.u_radius 
                # the structure of the collision_solids list will be: [temp1,temp2,...]
                # asteroids and irregular shapes must be slightly bigger than their hitbox in order to avoid visual glitches
                self.collision_solids[len(self.collision_solids)-1].CollisionNode.addSolid(self.collision_solids[len(self.collision_solids)-1].Volume) #I am definitely not explaining that
                temp=None
                if self.debug:
                    loadPrcFileData("", "show-frame-rate-meter  1")
                    self.collision_solids[len(self.collision_solids)-1].NodePath.show() # debugging purposes only
                
                print("collision: ok")
                print("placing body: done")
                if c.is_lightSource:
                    self.light_Mngr.append([PointLight(c.id+"_other")])
                    self.light_Mngr[len(self.light_Mngr)-1].append(self.Game_state.root_node.attachNewNode(self.light_Mngr[len(self.light_Mngr)-1][0]))
                    self.light_Mngr[len(self.light_Mngr)-1][1].setPos(tuple(c.position))
                    # shadow stuff
                    
                    self.light_Mngr[len(self.light_Mngr)-1][1].node().setShadowCaster(True)
                    '''
                    self.light_Mngr[len(self.light_Mngr)-1][1].node().getLens().setFov(40)
                    self.light_Mngr[len(self.light_Mngr)-1][1].node().getLens().setNearFar(10, 100)
                    '''
                    self.Game_state.root_node.setLight(self.light_Mngr[len(self.light_Mngr)-1][1]) 

                    self.light_Mngr.append([AmbientLight(c.id+"_self")])
                    self.light_Mngr[len(self.light_Mngr)-1][0].setColorTemperature(3000)
                    self.light_Mngr[len(self.light_Mngr)-1].append(self.Game_state.root_node.attachNewNode(self.light_Mngr[len(self.light_Mngr)-1][0]))
                    for u in range(0,len(c.filelist),2):
                        c.filelist[u].setLight(self.light_Mngr[len(self.light_Mngr)-1][1])
                    print("lights: done")
                else:
                    self.light_Mngr.append([]) #create an empty list, so that the coordinates of the data in the list is the same as in self.bodies (easier for further analysis and potential deletion)
                    self.light_Mngr.append([])
                
                print("loaded new body, out: done")
            if SKYBOX=='sky':
                self.isphere.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldCubeMap)  # *takes a deep breath* cubemap stuff !
                self.isphere.setTexProjector(TextureStage.getDefault(), self.Game_state.root_node, self.isphere)
                self.isphere.setTexPos(TextureStage.getDefault(), 0, 0, 0)
                self.isphere.setTexScale(TextureStage.getDefault(), .5) # that's a thing...
                self.isphere.setTexture(self.tex)# Create some 3D texture coordinates on the sphere. For more info on this, check the Panda3D manual.
                self.isphere.setLightOff()
                self.isphere.setScale(10000) #hope this is enough
                self.isphere.reparentTo(self.Game_state.root_node)
            elif SKYBOX=='arena':
                self.box.setPos(0,0,0)
                self.box.setScale(100)
                self.box.reparentTo(self.Game_state.root_node)
            # collision traverser and other collision stuff # that's super important, and super tricky to explain so just check the wiki
            self.ctrav = CollisionTraverser()
            self.queue = CollisionHandlerQueue()
            for n in self.collision_solids:
                self.ctrav.add_collider(n.NodePath,self.queue)
            # the traverser will be automatically updated, no need to repeat this every frame
            # debugging only
            if self.debug:
                self.ctrav.showCollisions(self.Game_state.root_node) 
            # play a random music
            self.current_playing=random.randint(0,len(self.sounds)-1)
            self.current_song=self.loader.loadSfx(self.sounds[self.current_playing])
            self.current_song.play()

            # task manager stuff comes here
            self.intro_loop()
        except:
            sys.exit(":( something went wrong: files could not be loaded")
        
        
        # key bindings
        self.accept('escape',self.toggle_pause)
        self.accept('mouse1',self.handle_select,[True])
        self.accept('wheel_up',self.handle_scrolling,[True]) # center button (just a quick test)
        self.accept('wheel_down',self.handle_scrolling,[False])
        self.accept('z',self.move_camera,[0,True])
        self.accept('q',self.move_camera,[1,True])
        self.accept('s',self.move_camera,[2,True])
        self.accept('d',self.move_camera,[3,True])
        self.accept('a',self.move_camera,[4,True])
        self.accept('e',self.move_camera,[5,True])
        self.accept('arrow_right',self.time_change,[True])
        self.accept('arrow_left',self.time_change,[False])
        
        self.accept('z-up',self.move_camera,[0,False])
        self.accept('q-up',self.move_camera,[1,False])
        self.accept('s-up',self.move_camera,[2,False])
        self.accept('d-up',self.move_camera,[3,False])
        self.accept('a-up',self.move_camera,[4,False])
        self.accept('e-up',self.move_camera,[5,False])
        self.keymap=['z',0,'q',0,'s',0,'d',0,'a',0,'e',0,'mouse1',0]
        
        
        if self.debug: 
            # draw axis
            coord=[(1,0,0),(0,1,0),(0,0,1)]
            axis=[]
            for c in range(3): 
                axis.append(LineSegs())
                axis[c].moveTo(0,0,0)
                axis[c].drawTo(coord[c])
                axis[c].setThickness(3)
                axis[c].setColor(tuple([coord[c][u]*255 for u in range(len(coord[c]))] +[True]))
                NodePath(axis[c].create()).reparent_to(self.Game_state.root_node)

        # camera positionning -------
        self.focus_point=[0,0,0] # point focused: can become a body's coordinates if the user tells the program to do so
        self.zoom_distance=30 # distance to the focus point in common 3D units (can be modified by scrolling)
        self.cam_Hpr=[0,0,0] # phi, alpha, theta - aka yaw, pitch, roll
        self.cam_Hpr=[self.cam_Hpr[n]*pi/180 for n in range(len(self.cam_Hpr))] # convert to rad
        phi,alpha,theta,zoom,object=self.cam_Hpr[0]*pi/180,self.cam_Hpr[1]*pi/180,self.cam_Hpr[2]*pi/180,self.zoom_distance,self.state[2] # temporary vars
        if self.state[1]=='free':
            self.camera_pos=[0,0,0]
            self.camera.setPos(tuple(self.camera_pos))
        elif self.state[1]=='linked':
            # find the object (self.state[2]) in the data list
            list_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(object.getParent())
            self.focus_point=self.bodies[list_pos].position # take the focused object's coordinates
            self.camera_pos=[self.focus_point[0]+sin(phi)*cos(-alpha)*zoom,self.focus_point[1]-cos(phi)*cos(-alpha)*zoom,self.focus_point[2]+sin(-alpha)*zoom] #keep it up to date so that it's not hard to find whend switching modes
            self.camera.setPos(tuple(self.camera_pos))
            self.camera.setHpr(self.cam_Hpr)

        # cursor
        self.cursor=self.showsimpletext('.',(0,0),(0.08,0.08),None,(1,1,1,True)) # yeah, you can laugh, but this still works so I don't care
        self.pointerNode=CollisionNode('cursor')
        self.pointerNP=camera.attachNewNode(self.pointerNode)
        self.pointerNode.setFromCollideMask(BitMask32.bit(1)) # separate collisions (in order to avoid mistakes during physical calculations)
        self.cursor_ray=CollisionRay() # create the mouse control ray
        self.pointerNode.addSolid(self.cursor_ray)
        self.ctrav.add_collider(self.pointerNP,self.queue)

        #self.screen_fill.destroy() # delete the displayed picture
        return None




    
    def showsimpletext(self,content,pos,scale,bg,fg): #shows a predefined, basic text on the screen (variable output only)
        return OnscreenText(text=content,pos=pos,scale=scale,bg=bg,fg=fg)
    
    def intro_loop(self):
        
        self.taskMgr.add(self.mouse_check,'mousePositionTask')
        self.taskMgr.add(self.placement_Mngr,'frameUpdateTask')
        self.taskMgr.add(self.Sound_Mngr,'MusicHandle')
        self.taskMgr.add(self.camera_update,'cameraPosition')
        self.taskMgr.remove('showIntroPic')
        return None
    
    def placement_Mngr(self,task): # void main = main game mechanics, frame updating function (kinda, all pausing and menu functions must be applied here)
        if self.state[0]=='running' or not task.time:
            self.ctrav.traverse(self.Game_state.root_node)
            #self.queue = CollisionHandlerQueue() # update the collision queue
            brakeforce=[0 for n in range(len(self.bodies))] # create an empty brakeforce list
            if self.queue.getNumEntries():
                if self.debug:
                    print(self.queue.getNumEntries()) # debug, shows only one digit most of the time
                # now we have to create a temp list containing only the Entries that refer to collisions between bodies,
                # not cursor-type collisions:
                temp1,temp2=[],[]
                for count in range(len(self.queue.getEntries())):
                    if self.queue.getEntries()[count].getFromNodePath()!=self.pointerNP: temp1.append(self.queue.getEntries()[count])
                    else: temp2.append(self.queue.getEntries()[count])
                # the temp1 and temp2 lists have been created 

                # run the check for the body-with-body collisions 
                if len(temp1)>len(self.stored_collisions):
                    print('[WARNING]: New collision') # debugging , detects when a collision occurs AND IT F*****G WORKS BITCH !! (actually I created this system so that the particles linked to the collision are only created once)
                    self.particle.add_particle(temp1[len(self.stored_collisions):])
                    #b=len(temp1[self.stored_collisions:len(temp1)])
                    for x in temp1[len(self.stored_collisions):]:
                        self.particle.activate(x.getIntoNodePath().getParent(),self.Game_state.root_node)
                elif len(temp1)<len(self.stored_collisions):
                    print('Collision ended')
                    a=self.stored_collisions[len(temp1):]
                    for x in a:
                        self.particle.deactivate(x.getIntoNodePath().getParent())
                        self.particle.deactivate(x.getFromNodePath().getParent())
                    # at this point we should probably delete the particle object, as if we don't, it might still be updated, even if the collision doesn't exist in the self.queue anymore
                self.stored_collisions=temp1 #else do nothing
                for c in range(0,len(temp1),2): 
                    entry=temp1[c]
                    brakeforce=self.collision_log(entry,brakeforce)
                
                # run the check for the cursor-with-body collisions
                for c in range(len(temp2)):
                    entry=temp2[c]
                    self.watched=entry.getIntoNodePath()
                # print "out"

                # update the collider list
                self.ctrav.clear_colliders()
                self.queue = CollisionHandlerQueue()
                for n in self.collision_solids:
                    self.ctrav.add_collider(n.NodePath,self.queue)
                self.ctrav.add_collider(self.pointerNP,self.queue) # add the cursor ray again
            else:
                self.watched=None
                
            # collision events are now under constant surveillance
            acceleration=[]
            for c in range(len(self.bodies)): #selects the analysed body
                var=self.bodies[c]
                Bdf=[0,0,0] #Bdf stands for 'bilan des forces' in french, it's the resulting acceleration
                for d in self.bodies[0:c]+self.bodies[c+1:len(self.bodies)-1]: #selects the body which action on the analysed body we're studying...not sure about that english sentence though
                    S,M=[d.mass]+d.position,[var.mass]+var.position
                    temp=self.dual_a(S,M)
                    Bdf=[Bdf[x]+temp[x] for x in range(3)] # list sum
                # add the result to the global save list
                acceleration.append(Bdf)
            #update the bodies' position
            self.speed_update(acceleration,brakeforce)
            self.pos_update()
            self.apply_update()
        elif self.state[0]=='paused':
            self.handle_menu(self.iteration)
            self.iteration+=1
        return task.cont
    
    def speed_update(self,a,brakeforce):
        for c in range(len(self.bodies)): #the function updates the speed tuple accordingly
            self.bodies[c].speed[0]+=self.timescale*a[c][0]
            #self.bodies[c].speed[0]/=brakeforce[c]+1 # aero/lytho braking has to be applied to the colliding object
            # actually, speed isn't applied that way
            self.bodies[c].speed[1]+=self.timescale*a[c][1]
            #self.bodies[c].speed[1]/=brakeforce[c]+1
            self.bodies[c].speed[2]+=self.timescale*a[c][2]
            #self.bodies[c].speed[2]/=brakeforce[c]+1 
        return 0
    
    def pos_update(self): #updates the positional coordinates
        for c in range(len(self.bodies)):
            self.bodies[c].position[0]+=self.timescale*self.bodies[c].speed[0]
            self.bodies[c].position[1]+=self.timescale*self.bodies[c].speed[1]
            self.bodies[c].position[2]+=self.timescale*self.bodies[c].speed[2]
        return 0
    
    def apply_update(self): #actually moves the hole 3d stuff around
        count=0 #local counter
        for c in self.bodies:
            for u in range(len(c.filelist)):
                if u%2!=0:
                    c.filelist[u-1].setHpr(c.filelist[u-1],tuple([self.timescale*i for i in c.filelist[u]]))
                else:    
                    c.filelist[u].setPos(tuple(c.position))    
            if c.is_lightSource:
                self.light_Mngr[count][1].setPos(tuple(c.position))
            count+=2
        return 0
    
    def camera_update(self,task):
        phi,alpha,theta,zoom,object=self.cam_Hpr[0]*pi/180,self.cam_Hpr[1]*pi/180,self.cam_Hpr[2]*pi/180,self.zoom_distance,self.state[2]
        if self.state[1]=='free':
            self.camera.setPos(tuple(self.camera_pos))
        elif self.state[1]=='linked':
            # find the object (self.state[2]) in the data list
            list_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(object.getParent())
            self.focus_point=self.bodies[list_pos].position # take the focused object's coordinates
            self.camera_pos=[self.focus_point[0]+sin(phi)*cos(-alpha)*zoom,self.focus_point[1]-cos(phi)*cos(-alpha)*zoom,self.focus_point[2]+sin(-alpha)*zoom]
            self.camera.setPos(tuple(self.camera_pos))
            self.camera.setHpr(tuple(self.cam_Hpr))
        
        # collision cursor stuff goes here:
        self.cursor_ray.setFromLens(self.camNode,0,0) 
        # relatively to the camera, the cursor position will always be 0,0 which is the position of the 
        # white point on the screen


        if self.keymap!=['z',0,'q',0,'s',0,'d',0]:
            for x in range(1,len(self.keymap),2):
                if self.keymap[x]:
                    self.move_camera(int((x-1)/2),True) # why (x-1)/2 ? because we have to make the tow readable as a key number, like 0,1,2,3
        return task.cont

    def dual_a(self,S,M): #S is the "static object", the one that applies the force to the "moving" object M
        O=[]  #This will be the list with the accelerations for an object 
        d=sqrt((S[1]-M[1])**2+(S[2]-M[2])**2+(S[3]-M[3])**2)
        x=(self.u_constant*S[0]*(S[1]-M[1]))/d**2
        y=(self.u_constant*S[0]*(S[2]-M[2]))/d**2
        z=(self.u_constant*S[0]*(S[3]-M[3]))/d**2
        O.append(x)
        O.append(y)
        O.append(z)
        return O
    
    def collision_log(self,entry,brakeforce):
        from_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(entry.getFromNodePath().getParent())
        into_pos=[self.bodies[n].filelist[0] for n in range(len(self.bodies))].index(entry.getIntoNodePath().getParent()) #find the nodepath in the list
        f_radius=sum(self.bodies[from_pos].scale)*self.u_radius/3
        i_radius=sum(self.bodies[into_pos].scale)*self.u_radius/3
        if max(f_radius,i_radius)==f_radius:
            inverted=True
            into_pos,from_pos=from_pos,into_pos
        else:
            inverted=False # currently useless
        brakeforce[from_pos]=self.bodies[from_pos].brakeforce # get the force given in the data list
        # those are the two positions of the nodepaths, now we need to know which one is bigger, in order to obtain the fusion effect
        # from_pos is the smaller body, into_pos is the bigger one
        self.collision_gfx(self.momentum_transfer(from_pos,into_pos,entry,inverted),f_radius,i_radius) #some useless data remains from version 0.9
        return brakeforce
    
    def momentum_transfer(self,f_pos,i_pos,entry,inverted):
        if self.debug:
            print("colliding") # debug, makes the game laggy (only activated when the self.debug var is on)
        interior = entry.getInteriorPoint(self.Game_state.root_node) # default
        surface = entry.getSurfacePoint(self.Game_state.root_node)
        if self.debug:
            print((interior - surface).length()) # debug, doesn't slow the game down too much so I haven't removed it


        if (interior - surface).length() >= self.u_radius*2*sum(self.bodies[f_pos].scale)/3: # this is the body deletion routine
            if self.state[2]==self.collision_solids[f_pos].NodePath:
                self.state[1]='free'
                self.state[2]=None

            # Lighting
            if self.bodies[f_pos].is_lightSource:
                self.Game_state.root_node.clearLight(self.light_Mngr[2*f_pos][1])
                self.Game_state.root_node.clearLight(self.light_Mngr[2*f_pos][1])
                self.filters.delVolumetricLighting() #temp
            
            self.ctrav.remove_collider(self.collision_solids[f_pos].NodePath)
            self.bodies[f_pos].delete_body()
            
            self.bodies[i_pos].scale[0]*=(self.bodies[i_pos].mass+self.bodies[f_pos].mass)/self.bodies[i_pos].mass
            self.bodies[i_pos].scale[1]*=(self.bodies[i_pos].mass+self.bodies[f_pos].mass)/self.bodies[i_pos].mass
            self.bodies[i_pos].scale[2]*=(self.bodies[i_pos].mass+self.bodies[f_pos].mass)/self.bodies[i_pos].mass
            self.bodies[i_pos].mass+=self.bodies[f_pos].mass
            
            # particle deletion

            self.particle.deactivate(self.collision_solids[f_pos].NodePath.getParent())
            self.particle.deactivate(self.collision_solids[i_pos].NodePath.getParent())
                
            # scale updating ()
            ''' temporarly removed
            for c in range(0,len(self.bodies[i_pos].filelist),2):
                self.bodies[i_pos].filelist[c].setScale(tuple(self.bodies[i_pos].scale))
            '''
            # deleting the destroyed planet's data
            self.bodies=self.bodies[:f_pos]+self.bodies[f_pos+1:len(self.bodies)]
            self.collision_solids=self.collision_solids[:f_pos]+self.collision_solids[f_pos+1:len(self.collision_solids)]
            # update the light list
            self.light_Mngr=self.light_Mngr[:2*f_pos]+self.light_Mngr[2*f_pos+2:len(self.light_Mngr)]
            
            
            # just a quick test
            if self.debug:
                self.ctrav.showCollisions(self.Game_state.root_node) 
            if self.debug:
                print("planet destroyed")
        return interior,surface # used for the collision gfx calculations
    
    def printScene(self):  #debug
        file=open("scenegraph.txt","a")
        ls = LineStream()
        self.Game_state.root_node.ls(ls)
        while ls.isTextAvailable():
            file.write(ls.getLine())
            file.write("\n")
        file.write("\n")
        file.write("END\n")
        file.write("\n")
        file.close()
    
    def Sound_Mngr(self,task):
        if self.current_song.length()-self.current_song.getTime()==0: #could have just used not()
            self.current_playing=random.choice(list(range(0,self.current_playing))+list(range(self.current_playing+1,len(self.sounds))))
            self.current_song=self.loader.loadSfx(self.sounds[self.current_playing])
            self.current_song.play()
            print(self.current_playing)
        return task.cont

    def collision_gfx(self,points,Rf,Ri): # collision animation calculations
        # section size calculation
        # we know the depth of penetration (no silly jokes please), which allows us, knowing the radius of each body, 
        # to calculate the radius of the section (I've got no idea how to say that in correct english)
        # the display of the particles all over this circle will be a piece of cake (at least I hope so)  - edit - it wasn't
        # see documents in the screenshot folder for more informations about the maths
        '''
        interior,surface=points[0],points[1]
        p=(interior - surface).length()
        p2=(p**2-2*Ri*p)/(2*Ri-2*p-2*Rf)
        p1=p-p2
        ''' #currently useless
        self.update_particle_pos()
        # now we know everything about our impact section (the circle that defines the contact between the two bodies)
        # we just have to find the coord of the circle's center: we will take the surfacepoint impact point
         
        return 0

    def create_crater(self): # see project for more informations
        return None

    def toggle_pause(self):
        self.toggle_particles() # pause the particles
        temp=['paused','running']
        self.state[0]=temp[self.state[0]==temp[0]] # switches between paused and running
        self.iteration=0
        if self.state[0]=='paused':
            # discord rpc updating
            try: 
                RPC.update(state="Version: 0.11", details="In the menus",large_image="logo",small_image=None)
            except: pass
            self.handle_menu(self.iteration)
        else:
            # discord RPC updating
            try: 
                RPC.update(state="Version: 0.11", details="In a simulation",large_image="logo",small_image=None)
            except: pass
            
            self.filters.del_blur_sharpen()
            self.filters.set_gamma_adjust(1)
            # make the mouse invisible
            self.hidden_mouse=True
            wp = WindowProperties()
            wp.setCursorHidden(self.hidden_mouse)
            # set the mouse pos to 0 
            self.center_mouse()

            self.win.requestProperties(wp)
            for u in self.paused_menu_text:
                u.hide()
        return None
    
    def handle_menu(self,iteration):
        if not iteration:
            self.accept('escape',self.toggle_pause)
            self.draw_menu()
            # make the mouse visible
            self.hidden_mouse=False
            wp = WindowProperties()
            wp.setCursorHidden(self.hidden_mouse)
            self.win.requestProperties(wp)
        else:
            a=1 # indentation (temporary)
            #put your mouse detection stuff here
            # menu stuff
            # menu stuff
            # please use directGui for that purpose (better rendering performances)
            # the menu doesn't actually work, as the whole clicking reaction routine is not implemented
        return None
    
    def draw_menu(self):
        self.filters.setBlurSharpen(amount=0)
        self.filters.set_gamma_adjust(1.7)
        for u in self.paused_menu_text:
                u.show()
        return None
    
    def show_credits(self):
        print("created by l3alr0g, zudo and Fang (at least this part) --> I'll do something better later")
        return None
        
    def system_break(self):
        # place your data saving routines here
        print("system exit successful, data saved")
        print("executing sys.exit()")
        print("out: done")
        sys.exit(0)
        return None
    
    def handle_scrolling(self,up): # up is a boolean: up=True means up, up=False means down
        if up and self.state[0]=='running':
            if self.state[1]=='linked':
                self.zoom_distance*=0.95
            else:
                self.camera_delta*=1.1
        elif not up and self.state[0]=='running':
            if self.state[1]=='linked':
                self.zoom_distance/=0.95
            else:
                self.camera_delta/=1.1
        return None

    
    def rotate_camera(self):
        self.camera.setHpr(tuple(self.cam_Hpr))
        return None
    
    def move_camera(self,tow,pressed): # tow stands for towards, pressed is a boolean which indicates the state of the key
        if pressed:
            self.keymap[2*tow+1]=1
            self.state[1]='free'
            #print('free mode on')
            self.state[2]=None
        else:
            self.keymap[2*tow+1]=0
        
        if self.keymap[2*tow+1]:
            phi,alpha,theta,delta,zoom=self.cam_Hpr[0]*pi/180,self.cam_Hpr[1]*pi/180,self.cam_Hpr[2]*pi/180,self.camera_delta,self.zoom_distance
            if self.keymap[2*tow]=='q':
                if self.state[1]=='free':
                    self.camera_pos=[self.camera_pos[0]-cos(phi)*cos(theta)*delta,self.camera_pos[1]-sin(phi)*cos(theta)*delta,self.camera_pos[2]+sin(theta)*delta] # moving the camera
            if self.keymap[2*tow]=='z':
                if self.state[1]=='free':
                    self.camera_pos=[self.camera_pos[0]-sin(phi)*cos(alpha)*delta,self.camera_pos[1]+cos(phi)*cos(alpha)*delta,self.camera_pos[2]+sin(alpha)*delta]
            if self.keymap[2*tow]=='s':
                if self.state[1]=='free':
                    self.camera_pos=[self.camera_pos[0]+sin(phi)*cos(alpha)*delta,self.camera_pos[1]-cos(phi)*cos(alpha)*delta,self.camera_pos[2]-sin(alpha)*delta]
            if self.keymap[2*tow]=='d':
                if self.state[1]=='free':
                    self.camera_pos=[self.camera_pos[0]+cos(phi)*cos(theta)*delta,self.camera_pos[1]+sin(phi)*cos(theta)*delta,self.camera_pos[2]-sin(theta)*delta]
            if self.keymap[2*tow]=='a':
                self.cam_Hpr[2]-=1
            if self.keymap[2*tow]=='e':
                self.cam_Hpr[2]+=1
        return None
    
    def mouse_check(self,task): # gets the mouse's coordinates
        mwn = self.mouseWatcherNode
        if mwn.hasMouse():
            x,y=mwn.getMouseX(),mwn.getMouseY()
            #print(x,y) # debug
            # focus_point coordinates modifier code here:
            if self.state==['running','free',None]:
                self.cam_Hpr[0]-=x*self.sensitivity_x # the - fixes a bug I can't solve
                # sensitivity is a coefficient used for mouse displacement routines
                self.cam_Hpr[1]+=y*self.sensitivity_y # those formulas do not work when theta (self.cam_Hpr[2]) changes 
                self.rotate_camera()
                self.center_mouse()
            elif self.state[0]=='running' and self.state[1]=='linked':
                self.cam_Hpr[0]-=x*self.sensitivity_x
                self.cam_Hpr[1]-=y*self.sensitivity_y
                self.rotate_camera()
                self.center_mouse()
            '''
            if self.debug:
                print(self.cam_Hpr,self.camera_pos) # debug
        '''
        return task.cont

    def center_mouse(self):
        self.win.movePointer(0,
          int(self.win.getProperties().getXSize() / 2),
          int(self.win.getProperties().getYSize() / 2)) # move mouse back to center --> careful ! this makes the delta calculation code buggy
    
    def handle_select(self,is_clicked): 
        if is_clicked and self.watched!=None:
            self.state[1]='linked' # toggle following mode
            self.state[2]=self.watched
            print('linked mode on, focusing: ',self.watched)
        #else: # do nothing actually
        return None
    
    def update_particle_pos(self): # harder than I thought
        for x in range(len(self.particle.particle_list)):
            a=[i.getIntoNodePath() for i in self.queue.getEntries()].index(self.particle.particle_list[x][1]) # finding the intonodepath inside self.queue.getEntries()
            self.particle.particle_list[x][0].setPos(self.queue.getEntries()[a].getSurfacePoint(self.Game_state.root_node)) # all particles are being displaced to the position of the surface impact point
            tempvar=self.queue.getEntries()[a].getSurfacePoint(self.Game_state.root_node) - self.queue.getEntries()[a].getIntoNodePath().getParent().getPos()
            H,P,R=-atan(tempvar[0]/tempvar[1])*180/pi+180,(-atan(tempvar[2]/tempvar[1])+pi/2)*180/pi,0
            self.particle.particle_list[x][0].setHpr(H,P,R)
        # self.queue.getEntries()[a].getIntoNodePath().getParent().getPos()
        # +self.queue.getEntries()[a].getSurfacePoint(self.queue.getEntries()[a].getIntoNodePath())  
        
        return None
    
    def time_change(self,income): # income is a boolean, True means speed up, False means speed down
        if income:
            self.timescale*=1.2
        else:
            self.timescale*=0.80
        return None
    
    def not_implemented_yet(self): # comes with self.follow function
        self.sign=OnscreenImage(image=str(MAINDIR)+"/Engine/not_implemented_yet.png",pos=(0,0,0),scale=(0.5,0.5,0.5))  # scale is useless: already at scale (the pic is square shaped)
        self.sign.setTransparency(TransparencyAttrib.MAlpha)
        self.accept("escape",self.follow)
        self.quit_button['state']=DGG.DISABLED
        self.settings_button['state']=DGG.DISABLED
        self.start_button['state']=DGG.DISABLED
        return None
    
    def follow(self): # dependencies of the not_implemented_yet function
        self.ignore("escape")
        self.sign.destroy()
        self.quit_button['state']=DGG.NORMAL
        self.settings_button['state']=DGG.NORMAL
        self.start_button['state']=DGG.NORMAL
        return None

    def easter_egg(self):
        return "please be patient, our hens are working on it" # I am not responsible for the bad quality of my humor
    
    def ingame_back_to_menu(self): # large name, I know, I had no ideas
        self.filters.set_gamma_adjust(1)
        for u in self.paused_menu_text:
            u.hide()
        self.filters.del_blur_sharpen()
        self.Game_state.cleanup()
        # we have to delete all the taskManager routines we implemented during the simulation
        # here comes the whole stuff:
        self.taskMgr.remove('mousePositionTask')
        self.taskMgr.remove('frameUpdateTask')
        self.taskMgr.remove('MusicHandle')
        self.taskMgr.remove('cameraPosition')
        # end of task manager stuff
        self.stored_collisions=[]
        self.watched=None
        self.state=['paused','free',None]
        self.iteration=0

        self.filters.delVolumetricLighting() # temporary, as I have to implement multiple light source handling

        # music 
        self.current_song.stop()
        
        self.menu()
        return None