Example #1
0
	def makeFullscreen(self):
		# self.window = sdl2.ext.Window(self.title, size=(self.res_x, self.res_y), flags=sdl2.SDL_WINDOW_FULLSCREEN)

		current_display = sdl2.SDL_GetWindowDisplayIndex(self.window.window)
		current_display_mode = sdl2.SDL_DisplayMode()
		res = sdl2.SDL_GetCurrentDisplayMode(current_display,current_display_mode)
		px_log.log(f"Current display mode: {current_display_mode}")

		display_modes = []
		display_mode_index = 0  # sdl2.SDL_GetWindowDisplayIndex(self.window.window)
		for mode_index in range(current_display, sdl2.SDL_GetNumDisplayModes(display_mode_index)):
			disp_mode = sdl2.SDL_DisplayMode()
			ret = sdl2.SDL_GetDisplayMode(
				display_mode_index,
				mode_index,
				disp_mode
			)
			display_modes.append(disp_mode)
			px_log.log(f"mode:{mode_index} info:{display_modes[mode_index]}")

		# todo: something more clever than this after testing on more monitors
		# prob just make a better guess on the window size as a whole fraction of the native display size
		# without getting too large or too small
		# e.g. for MacBook Pro, 960 is too small; 1280 isn't great and is more fuzzy than 1920 - which looks fine;
		# 2880 looks even sharper than as 1920, but will prob be pushing too many pixels
		# Prob should allow it to be overridden via options/config

		# sdl2.SDL_SetWindowSize(self.window.window,1280,720)
		# sdl2.SDL_SetWindowSize(self.window.window,1440,810)
		# sdl2.SDL_SetWindowSize(self.window.window,1920,1080)
		sdl2.SDL_SetWindowSize(self.window.window,
													 self.res_x*self.zoom,
													 self.res_y*self.zoom)
		sdl2.SDL_SetWindowFullscreen(self.window.window, sdl2.SDL_WINDOW_FULLSCREEN)
		return
Example #2
0
def run(tests=False):
	game = PacBun()
	if tests:
		game.runTests()
	game.run()
	px_log.log("Exiting...")
	px_log.flushToFile()
	return 0
Example #3
0
	def makeTemplates(self, templates_data):
		for name, template in templates_data.items():
			px_log.log(f"Making {name} template.")
			self.entity_manager.makeEntityTemplate(name,
																						 controller=template['controller'](self.controller_manager) if 'controller' in template else None,
																						 collider=template['collider'](self.controller_manager) if 'collider' in template else None,
																						 graphics=self.graphics_manager.makeTemplate(template['graphics']['component'],
																																												 {'RenderLayer': self.render_layers[template['graphics']['render layer']]}) if 'graphics' in template else None
																						 )
Example #4
0
 def renderTextToSurfaceAndSave(self,
                                file,
                                string,
                                font=False,
                                color=px_graphics.Color(1, 1, 1, 1)):
     surf = sdl2.sdlttf.TTF_RenderUTF8_Solid(self.sdl_fonts[font].font,
                                             string.encode('utf-8'), color)
     if surf is None:
         log(f"TTF_RenderText failed: {string}")
     width = surf.contents.w
     height = surf.contents.h
     return width, height
Example #5
0
 def initEntity(self, entity, data=False):
     entity.message = False
     if data:
         entity.pos = data['pos']
         entity.parent = entity.game.getEntityByName(f'bunny choice {0}')
         entity.bun_num = data['bun num']
         bunnies = entity.game.game_data['bunnies']
         entity.message_color = bunnies.index(entity.bun_num).color
         entity.bun_name = data['bun name']
     else:
         px_log.log(
             "*** Warning: Entity {entity.name} with BunnyChooseController component missing data."
         )
Example #6
0
 def update(self, entity, time):
     if entity.new_state:
         entity.new_state = False
         if entity.state in self.anims:
             entity.current_anim = entity.state
             entity.current_state = entity.state
             self.anims[entity.current_anim].startAnim(
                 entity
             )  # TODO allow some anims to begin from different frame
         else:
             log(f"Warning: {entity.name} animation doesn't exist for requested state {entity.state}"
                 )
     self.anims[entity.current_anim].advanceAnim(entity, time)
Example #7
0
    def makeAtlas(self, start_size=256):
        # tries this size and increases dimensions until the images all fit
        atlas_dim = start_size
        while not self._renderAtlas(atlas_dim, dry_run=True):
            atlas_dim = int(atlas_dim + 64)
            if atlas_dim > 4096:
                log("WARNING: a texture atlas dimension bigger than 4K may not work on some machines"
                    )

        # actually render atlas - do again with rendering and updating the image records
        self._renderAtlas(atlas_dim, dry_run=False)
        self.atlas_dim = atlas_dim
        log(f"Graphics|RenderLayer:Atlas created at size: {self.atlas_dim}")
Example #8
0
    def _renderAtlas(self, atlas_dim, dry_run, gap=0):
        if not dry_run:
            # make the atlas texture
            self.TA = sdl2.SDL_CreateTexture(self.ren.renderer,
                                             sdl2.SDL_PIXELFORMAT_ABGR8888,
                                             sdl2.SDL_TEXTUREACCESS_TARGET,
                                             atlas_dim, atlas_dim)
            # point drawing at the atlas and clear it
            sdl2.SDL_SetRenderTarget(self.ren.renderer, self.TA)
            sdl2.SDL_SetRenderDrawColor(self.ren.renderer, 0, 0, 0, 0)
            sdl2.SDL_RenderClear(self.ren.renderer)
            sdl2.SDL_SetRenderDrawColor(self.ren.renderer, 255, 255, 255, 255)

        # draws textures in rows, left to right, then top to bottom
        accum_x = 0  # where we've got to across TA
        max_y = 0  # how tall the row is so we know where to start the next one
        accum_y = 0  # how far down the current row is
        for image in sorted(self.images, key=Image.getHeight, reverse=True):
            if accum_x + image.width + gap > atlas_dim:  # will this image go off right of atlas?
                accum_x = 0
                # push down
                accum_y += max(
                    max_y,
                    image.height) + gap  # in case this is the tallest image
                max_y = 0

            if not dry_run:
                # draw into atlas and kill old texture
                image.draw(accum_x, accum_y)
                sdl2.SDL_DestroyTexture(image.texture)
                # and update the image to point to where it went in the atlas
                # update in place to avoid re-doing indexes
                image.texture = self.TA
                image.src = sdl2.SDL_Rect(accum_x, accum_y, image.width,
                                          image.height)

            accum_x += image.width + gap
            max_y = max(max_y, image.height)
            if accum_y + max_y > atlas_dim:
                log(f"Atlas overflow at size {atlas_dim}")
                return False

        if not dry_run:
            # point drawing at screen again
            sdl2.SDL_SetRenderTarget(self.ren.renderer, None)
            sdl2.SDL_SetTextureBlendMode(self.TA, sdl2.SDL_BLENDMODE_BLEND)
        return True
Example #9
0
    def addImageFromFile(self, file, trim=False):
        # find if this file has been loaded before and return that if so
        # also check if there's an index that is empty and can be re-used
        filepath = os.path.abspath(file)
        for index, image in enumerate(self.images):
            if image:
                if image.file == filepath:
                    image.ref_count += 1
                    return index, image.trim_x, image.trim_y

        # otherwise find next empty slot (or append)
        index = self._getNextEmptySlot()
        try:
            self.images[index] = Image.fromFile(self.ren,
                                                filepath,
                                                width=None,
                                                height=None,
                                                trim=trim)
            return index, self.images[index].trim_x, self.images[index].trim_y
        except Exception as e:
            log("Problem loading image for frame: " + str(e) + " file:" +
                filepath)
Example #10
0
    def renderText(self,
                   string,
                   font=False,
                   color=px_graphics.Color(1, 1, 1, 1)):
        #We need to first render to a surface as that's what TTF_RenderText
        #returns, then load that surface into a texture

        surf = sdl2.sdlttf.TTF_RenderUTF8_Solid(self.sdl_fonts[font].font,
                                                string.encode('utf-8'),
                                                color.toSDLColor())
        if surf is None:
            log(f"TTF_RenderText failed: {string}")
            return None
        texture = sdl2.SDL_CreateTextureFromSurface(self.ren.sdlrenderer, surf)
        if texture is None:
            print("CreateTexture")
        #Clean up the surface and font
        width = surf.contents.w
        height = surf.contents.h
        sdl2.SDL_FreeSurface(surf)
        # sdl2.sdlttf.TTF_CloseFont(font)
        return texture, width, height
Example #11
0
	def checkCollide(self,A,B):

		# progressive bounding box
		# check x first
		Apos = A.getPos()
		Adim = A.dim
		Aorig = A.orig
		Bpos = B.getPos()
		Bdim = B.dim
		Borig = B.orig

		if (Apos.x - Aorig.x + Adim.x)> (Bpos.x -Borig.x): # Aright > Bleft
			if (Bpos.x - Borig.x + Bdim.x) > (Apos.x - Aorig.x): # Bright < Aleft
				if (Apos.z - Aorig.z + Adim.z) > (Bpos.z - Borig.z):
					if (Bpos.z - Borig.z + Bdim.z) > (Apos.z - Aorig.z):
						if (Apos.y - Aorig.y + Adim.y) > (Bpos.y - Borig.y):
							if (Bpos.y - Borig.y + Bdim.y) > (Apos.y - Aorig.y):
								# we have a collision

								if collision_debug:
									log(f"Collision - A: {A.name} B: {B.name}")
								return True
Example #12
0
	def __init__(self):
		px_log.log("Getting game data...")
		self.game_data = px_utility.getDataFromFile('game.config')['game']

		px_log.log("Getting user data...")
		self.user_data = px_utility.getDataFromFile('user.config')['user']

		px_log.log("Setting up window...")
		# Initialize the video system - this implicitly initializes some
		# necessary parts within the SDL2 DLL used by the video module.
		#
		# You SHOULD call this before using any video related methods or
		# classes.
		sdl2.ext.init()

		self.title = self.game_data['title']
		self.res_x = self.game_data['res_x']
		self.res_y = self.game_data['res_y']
		self.zoom = self.user_data['zoom']
		self.fullscreen = self.user_data['fullscreen']
		self.clear_color =  self.game_data['clear_color']

		sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_JOYSTICK | sdl2.SDL_INIT_GAMECONTROLLER)

		# INITIALISE GRAPHICS & THE SCREEN

		# Create a new window (like your browser window or editor window,
		# etc.) and give it a meaningful title and size. We definitely need
		# this, if we want to present something to the user.
		# todo: calculate resolution more carefully esp for fullscreen

		# create the window (without showing it)
		self.windowed_width = self.res_x*self.zoom		# keep these for later
		self.windowed_height = self.res_y*self.zoom
		self.window = sdl2.ext.Window(self.title, size=(self.windowed_width, self.windowed_height))
		if(self.fullscreen):
			self.makeFullscreen()

		# set up a renderer to draw stuff. This is a HW accelerated one.
		# Switch on VSync to avoid running too fast
		# and wasting power and keep graphics nice and clean
		self.ren = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED | sdl2.SDL_RENDERER_PRESENTVSYNC)
		# makes zoomed graphics blocky (for retro effect)
		sdl2.SDL_SetHint(sdl2.SDL_HINT_RENDER_SCALE_QUALITY, b"nearest")
		# makes the graphics look and act like the desired screen size, even though they may be rendered at a different one
		sdl2.SDL_RenderSetLogicalSize(self.ren.renderer, self.res_x, self.res_y)

		self.running = True
		self.game_mode = eGameModes.title

		self.input = px_game_pad.Input(self)

		self.render_layers = {}
		self.scroll = False
		self.flags={}

		self.drawables = px_entity.EntityList()
		self.audibles = px_entity.EntityList()
		self.updatables = px_entity.EntityList()

		self.graphics_manager = px_entity.ComponentManager(game=self)
		self.controller_manager = px_entity.ComponentManager(game=self)
		self.entity_manager = px_entity.EntityManager(game=self)
		self.sound_manager = px_entity.ComponentManager(game=self)
		self.sound_mixer = px_sound.SoundMixer(self)
		self.collision_manager = px_collision.CollisionManager(game=self)

		# By default, every Window is hidden, not shown on the screen right
		# after creation. Thus we need to tell it to be shown now.
		self.window.show()
Example #13
0
	def nextScene(self, next_scene=-1, mode=False): # kill
		gc.enable()
		gc.collect()

		# clear all the flags
		self.flags={}

		if mode:
			if mode!=self.current_mode:
				px_log.log(f"Switching to mode: {mode}")
				self.current_mode=mode
				self.mode_data = self.game_data['modes'][self.current_mode] # convenience
				# kill old mode
				self.killEntitiesExceptDicts(
					[
						self.game_data['entities'],
					]
				)

				# set up next mode
				px_log.log(f"Making {mode} mode templates.")
				if 'templates' in self.mode_data:
					self.makeTemplates(self.mode_data['templates'])
				px_log.log(f"Making {mode} mode entities.")
				if 'entities' in self.mode_data:
					self.makeEntities(self.mode_data['entities'])
				if next_scene<0:	# no scene specified so revert to 0
					next_scene = 0
				px_log.flushToFile()



		if next_scene>=0:
			# specified scene rather than following pre-defined order
			self.current_scene = next_scene
			specified = "specified "
		else:
			self.current_scene+=1
			if self.current_scene>=len(self.mode_data['scenes']):
				# todo add check for completing the game instead of just looping?
				self.current_scene=0
			specified = ""
		next_scene = self.mode_data['scenes'][self.current_scene]
		px_log.log(
			f"Switching to {specified}scene [{self.current_mode},{self.current_scene}]: {self.mode_data['scenes'][self.current_scene]}")

		# kill old scene
		self.killEntitiesExceptDicts(
			[
				self.game_data['entities'],
			 	self.mode_data['entities'] if 'entities' in self.mode_data else {}
			]
		)

		###################
		# init next scene #
		###################
		self.scene_data = self.scenes_data['scenes'][self.mode_data['scenes'][self.current_scene]]
		# initialise map todo: make another entity instead of special
		# if "Map" in self.scene_data:
		# 	self.level = map.Map(self, self.scene_data, self.templates['tile'])

		if 'templates' in self.scene_data:
			self.makeTemplates(self.scene_data['templates'])
		if 'entities' in self.scene_data:
			self.makeEntities(self.scene_data['entities'])
		px_log.flushToFile()

		gc.collect()
		gc.disable()
		if len(gc.garbage)>0: px_log.log(gc.garbage)
Example #14
0
 def delete(self, data):
     px_log.log(f"Missing delete function for Component:{type(self)}")
Example #15
0
	def __init__(self):
		super(PacBun, self).__init__()
		sdl2.mouse.SDL_ShowCursor(False)
		px_log.log("Window set up.")

		px_log.log("Setting up render layers...")
		for rl_name,rl_data in self.game_data['render layers'].items():
			rl = px_graphics.RenderLayer(self.ren)
			self.render_layers[rl_name]=rl
			if 'fonts' in rl_data:
				px_log.log(f"	Getting fonts in Render Layer {rl_name}...")
				for font_name, font_data in rl_data['fonts'].items():
					# todo keep a handle on the fonts returned
					px_log.log(f"		Getting font: {font_name}...")
					rl.addFont(font_data['file'],font_data['size'])
		px_log.log("Render Layers set up.")

		# todo: consider moving this into px_game
		px_log.log("Making game scope templates...")
		self.templates = self.makeTemplates(self.game_data['templates'])
		# self.templates.update(self.makeTemplates(overlay_templates_data, self.overlay_renlayer))
		px_log.log("Templates made.")

		px_log.log("Making game scope entities...")
		self.makeEntities(self.game_data['entities'])
		px_log.log("Game scope entities made.")

		px_log.log("Getting scenes...")
		self.getScenesData('PB_scenes.config')

		# put all separate images into texture atlasses for (more) efficient rendering
		px_log.log("Making texture atlases...")
		for rl_name, rl_instance in self.render_layers.items():
			if 'texture atlas' in self.game_data['render layers'][rl_name]:
				# make texture atlas
				# todo: do stuff with texture atlas files
				if 'size hint' in self.game_data['render layers'][rl_name]:
					rl_instance.makeAtlas(self.game_data['render layers'][rl_name]['size hint'])
				else:
					rl_instance.makeAtlas()
				rl_instance.dumpAtlasToFiles(f"{rl_name}.png",f"{rl_name}.json")

		# self.renlayer.dumpAtlasToFiles("TA.png", "TA.json")
		px_log.log("### Startup complete ###")
		px_log.flushToFile()

		self.current_mode=-1
		self.nextScene(next_scene=0, mode=self.game_data['init_mode'])
Example #16
0
    def initEntity(self, entity, data=False):
        entity.won = False  # keeps track of if the map is complete or not
        impassables = ['H', '#', '<', '>']

        entity.map = copy.deepcopy(
            data)  # deepcopy since we're going to flip it upside down

        # flip since we draw from bottom left
        for i in range(0, 9):
            entity.map[i], entity.map[16 - i] = entity.map[16 -
                                                           i], entity.map[i]

        entity.tiles = [None] * 17
        for i in range(0, 17):
            entity.tiles[i] = [None] * 30

        entity.holes = []
        entity.bunnies = []
        entity.fox_starts = []

        entity.num_spaces = 0
        entity.num_poos = 0
        bunny_to_place = 0
        for y in range(0, 17):
            for x in range(0, 30):
                if entity.map[y][x] == '0':
                    continue
                # create tile for each square with anything in it
                this_tile = entity.game.requestNewEntity('tile',
                                                         pos=Vec3(
                                                             8 + x * 16,
                                                             8 + y * 16, 1),
                                                         parent=self,
                                                         name=f"back{x},{y}")
                entity.tiles[y][x] = this_tile

                # set up each tile from what the map says
                if entity.map[y][x] == "H":
                    this_tile.setState(tile.eTileStates.hedge)
                elif entity.map[y][x] == "#":
                    this_tile.setState(tile.eTileStates.void)
                else:
                    # work out ways out of the space
                    exit_map_value = 0
                    if y + 1 < 17:
                        if entity.map[y + 1][x] not in impassables:
                            this_tile.components['controller'].addExit(
                                this_tile, px_entity.eDirections.up)
                            exit_coord = Vec3(x * 16 + 8, y * 16 + 10, 0)
                            exit_direction = px_entity.eDirections.up
                            exit_map_value += 1
                    if y - 1 > 0:
                        if entity.map[y - 1][x] not in impassables:
                            this_tile.components['controller'].addExit(
                                this_tile, px_entity.eDirections.down)
                            exit_coord = Vec3(x * 16 + 8, y * 16 + 6, 0)
                            exit_direction = px_entity.eDirections.down
                            exit_map_value += 2
                    if x - 1 > 0:
                        if entity.map[y][x - 1] not in impassables:
                            this_tile.components['controller'].addExit(
                                this_tile, px_entity.eDirections.left)
                            exit_coord = Vec3(x * 16 + 6, y * 16 + 8, 0)
                            exit_direction = px_entity.eDirections.left
                            exit_map_value += 4
                    if x + 1 < 30:
                        if entity.map[y][x + 1] not in impassables:
                            this_tile.components['controller'].addExit(
                                this_tile, px_entity.eDirections.right)
                            exit_coord = Vec3(x * 16 + 10, y * 16 + 8, 0)
                            exit_direction = px_entity.eDirections.right
                            exit_map_value += 8

                    if entity.map[y][x] == "o":
                        this_tile.components['controller'].setState(
                            this_tile, tile.eTileStates.hole)
                        entity.holes.append(
                            Hole(pos=Vec3(x, y, 0),
                                 exit=exit_coord,
                                 direction=exit_direction))
                    elif entity.map[y][x] == "O":
                        this_tile.components['controller'].setState(
                            this_tile, tile.eTileStates.cutscene_hole)
                    elif entity.map[y][x] == "T":
                        # map to which tunnel graphic to use based on exits
                        # which_tunnel = [
                        # 	tile.eTileStates.tunnel_no_exit,  # 0000
                        # 	tile.eTileStates.tunnel_up,  # 0001
                        # 	tile.eTileStates.tunnel_down,  # 0010, 2
                        # 	tile.eTileStates.tunnel_up_down,  # 0011, 3
                        # 	tile.eTileStates.tunnel_left,  # 0100, 4
                        # 	tile.eTileStates.tunnel_up_left,  # 0101, 5
                        # 	tile.eTileStates.tunnel_down_left,  # 0110, 6
                        # 	tile.eTileStates.tunnel_up_down_left,  # 0111, 7
                        # 	tile.eTileStates.tunnel_right,  # 1000, 8
                        # 	tile.eTileStates.tunnel_up_right,  # 1001, 9
                        # 	tile.eTileStates.tunnel_down_right,  # 1010,10
                        # 	tile.eTileStates.tunnel_up_down_right,  # 1011,11
                        # 	tile.eTileStates.tunnel_left_right,  # 1100,12
                        # 	tile.eTileStates.tunnel_up_left_right,  # 1101,13
                        # 	tile.eTileStates.tunnel_down_left_right,  # 1110,14
                        # 	tile.eTileStates.tunnel_up_down_left_right,  # 1111,15
                        # ][exit_map_value]

                        if exit_map_value > 15:
                            px_log.log(
                                f"warning: exit map value boo boo {exit_map_value}"
                            )
                        this_tile.components['controller'].setState(
                            this_tile,
                            tile.eTileStates.tunnel_no_exit + exit_map_value)
                    else:
                        if entity.map[y][x] == "B":
                            if bunny_to_place < entity.game.getNumBunnies():
                                current_bunny_data = entity.game.getCurrentBunnyData(
                                    bunny_to_place)
                                entity.bunnies.append(
                                    entity.game.requestNewEntity(
                                        current_bunny_data.handle,
                                        Vec3(x * 16 + 8, y * 16 + 8, 0),
                                        parent=entity,
                                        name=current_bunny_data.handle,
                                        data={
                                            'game_pad':
                                            entity.game.getGamePad(
                                                bunny_to_place),
                                        }))
                            bunny_to_place += 1
                        elif entity.map[y][x] == "1":
                            # entity.fox_starts.append(Fox(pos=Vec3(x * 16 + 8, y * 16 + 8, 0),type=fox.eFoxTypes.direct))
                            entity.game.requestNewEntity(
                                'fox',
                                pos=Vec3(x * 16 + 8, y * 16 + 8, 0),
                                parent=entity,
                                name=
                                'fox 1',  # todo: consider numbering per fox not just per type
                                data={
                                    'type': fox.eFoxTypes.direct,
                                })
                        elif entity.map[y][x] == "2":
                            # entity.fox_starts.append(Fox(Vec3(x * 16 + 8, y * 16 + 8, 0),fox.eFoxTypes.axis_swap))
                            entity.game.requestNewEntity(
                                'fox',
                                pos=Vec3(x * 16 + 8, y * 16 + 8, 0),
                                parent=entity,
                                name=
                                'fox 2',  # todo: consider numbering per fox not just per type
                                data={
                                    'type': fox.eFoxTypes.ahead,
                                })
                        elif entity.map[y][x] == "3":
                            # entity.fox_starts.append(Fox(Vec3(x * 16 + 8, y * 16 + 8, 0),fox.eFoxTypes.ahead))
                            entity.game.requestNewEntity(
                                'fox',
                                pos=Vec3(x * 16 + 8, y * 16 + 8, 0),
                                parent=entity,
                                name=
                                'fox 3',  # todo: consider numbering per fox not just per type
                                data={
                                    'type': fox.eFoxTypes.axis_swap,
                                })
                        elif entity.map[y][x] == "4":
                            # entity.fox_starts.append(Fox(Vec3(x * 16 + 8, y * 16 + 8, 0),fox.eFoxTypes.cowardly))
                            entity.game.requestNewEntity(
                                'fox',
                                pos=Vec3(x * 16 + 8, y * 16 + 8, 0),
                                parent=entity,
                                name=
                                'fox 4',  # todo: consider numbering per fox not just per type
                                data={
                                    'type': fox.eFoxTypes.cowardly,
                                })
                        # blank space
                        entity.num_spaces += 1
                        this_tile.setState(tile.eTileStates.path)