def add_rect(self, new_rect: pygame.Rect): for sheet in Spritesheet.select(): rects = [pygame.Rect(t.x,t.y,t.width,t.height) for t in sheet.thumbnails] sheet_rect = pygame.Rect(0,0,sheet.height, sheet.width) for rect in rects: crects = rects.copy() crects.remove(rect) new_rect.topleft = rect.topright if new_rect.collidelist(crects) == -1: if sheet_rect.contains(new_rect): return sheet, new_rect new_rect.topleft = rect.bottomright if new_rect.collidelist(crects) == -1: if sheet_rect.contains(new_rect): return sheet, new_rect new_rect.topright = rect.topleft if new_rect.collidelist(crects) == -1: if sheet_rect.contains(new_rect): return sheet, new_rect new_rect.topright = rect.bottomleft if new_rect.collidelist(crects) == -1: if sheet_rect.contains(new_rect): return sheet, new_rect sheet = Spritesheet.create(height=DEFAULT_HEIGHT, width=DEFAULT_WIDTH) new_rect.topleft = (0, 0) return sheet, new_rect
def update_world_window(self, screen, floor_s, map_data): blit_area = Rect(0,0,64,64) # Get the dirty tile -coordinates from map_data for dirty_tile_position in map_data.get_dirty_tile_positions: # Calculate the screen position for given tile dirty_screen_position = screen_position_of_tile(dirty_tile_position) area_x, area_y = dirty_tile_position blit_area.topleft = (area_x*64, area_y*64) # Blit the tile from floor, to screen. screen.blit(floor_s, dirty_screen_position, blit_area) # Blit items in the tile # Try if this position contains a list of items. Note: these may fail :s items_in_position = map_data.get_items_on_position(dirty_tile_position) if items_in_position is not None: self.draw_items_on_tile(screen, items_in_position, dirty_screen_position) # Blit character in here. character = map_data.get_character_on_position(dirty_tile_position) if character is not None: screen.blit(character.surface, dirty_screen_position) map_data.reset_dirty_tiles()
def RenderRoundedRectangle(surface, position, dimensions, color, radius=0.5): targetRectangle = Rect(tuple(position), tuple(dimensions)) alpha = color[3] color = (color[0], color[1], color[2], 0) position = targetRectangle.topleft targetRectangle.topleft = 0, 0 circle = Surface([min(targetRectangle.size) * 3] * 2, SRCALPHA) draw.ellipse(circle, (0, 0, 0), circle.get_rect(), 0) circle = transform.smoothscale( circle, [int(min(targetRectangle.size) * radius)] * 2) rectangle = Surface(targetRectangle.size, SRCALPHA) radius = rectangle.blit(circle, (0, 0)) radius.bottomright = targetRectangle.bottomright rectangle.blit(circle, radius) radius.topright = targetRectangle.topright rectangle.blit(circle, radius) radius.bottomleft = targetRectangle.bottomleft rectangle.blit(circle, radius) rectangle.fill((0, 0, 0), targetRectangle.inflate(-radius.w, 0)) rectangle.fill((0, 0, 0), targetRectangle.inflate(0, -radius.h)) rectangle.fill(color, special_flags=BLEND_RGBA_MAX) rectangle.fill((255, 255, 255, alpha), special_flags=BLEND_RGBA_MIN) return surface.blit(rectangle, position)
def __init__( self, screen: pygame.surface.Surface, manager: pygame_gui.UIManager, rect: pygame.Rect, bg: ty.Tuple[int, int, int] = (150, 150, 150), ) -> None: self.screen = screen self.manager = manager self.rect = rect self.midi_in = SingleNoteInput() self.midi_in.init() self.bg = bg dropdown_rect = (0, 0, 200, MIDI_HEIGHT) rect = pygame.Rect(*dropdown_rect) rect.topleft = (self.rect.x, self.rect.y) keys = list(self.midi_in.apis.keys()) self.api = pygame_gui.elements.UIDropDownMenu( keys, keys[0], rect, self.manager ) rect = pygame.Rect(*dropdown_rect) rect.topright = (self.rect.x + self.rect.width, self.rect.y) keys = list(self.midi_in.ports) self.port = pygame_gui.elements.UIDropDownMenu( keys, keys[0], rect, self.manager )
def update_world_window(self, screen, world_s, camera, floor_s, map_data): blit_area = Rect(0, 0, 64, 64) # Get the dirty tile -coordinates from map_data for dirty_tile_position in map_data.get_dirty_tile_positions: # Calculate the screen position for given tile dirty_world_position = world_pos_of_tile(dirty_tile_position) area_x, area_y = dirty_tile_position blit_area.topleft = (area_x * 64, area_y * 64) # Blit the tile from floor, to screen. world_s.blit(floor_s, dirty_world_position, blit_area) # Blit items in the tile # Try if this position contains a list of items. Note: these may fail :s items_in_position = map_data.get_items_on_position(dirty_tile_position) if items_in_position is not None: self.draw_items_on_tile(world_s, items_in_position, dirty_world_position) # Blit character in here. character = map_data.get_character_on_position(dirty_tile_position) if character is not None: world_s.blit(character.surface, dirty_world_position) camera_rectangle = camera.get_viewport_rect assert isinstance(camera_rectangle, Rect) screen.blit(world_s, (16,16), area=world_rect_of_tile_rect(camera_rectangle)) map_data.reset_dirty_tiles()
def test_topleft(self): """Changing the topleft attribute moves the rect and does not change the rect's size """ r = Rect(1, 2, 3, 4) new_topleft = (r.left + 20, r.top + 30) old_size = r.size r.topleft = new_topleft self.assertEqual(new_topleft, r.topleft) self.assertEqual(old_size, r.size)
def test_topleft( self ): """Changing the topleft attribute moves the rect and does not change the rect's size """ r = Rect( 1, 2, 3, 4 ) new_topleft = (r.left+20,r.top+30) old_size = r.size r.topleft = new_topleft self.assertEqual( new_topleft, r.topleft ) self.assertEqual( old_size, r.size )
def rect_to_screen(self, rect, coords): """ Convert a rectangle into screen coordinates. """ if coords == Renderer.COORDS_SCREEN: return rect else: tl = self.world_to_screen(rect.topleft) br = self.world_to_screen(rect.bottomright) ret = Rect() ret.topleft = tl ret.bottomright = br return ret
def new_enemy(style, player, container): spawn = random.randint(1, config.game["spawnrate"]) spawn_rate = {"z": zombie_spawn, "s": skeleton_spawn} if not spawn > spawn_rate[style]: return coin = random.randint(0, 10) enemy = config.enemies[style].copy() sight = {"z": zombie_sight, "s": skeleton_sight} enemy["sight"] = sight[style] enemy["awake"] = coin <= config.game["awareness"] enemy["rect"] = Rect(0, 0, enemy["size"], enemy["size"]) enemy["rect"].topleft = (random.randint( 1, config.game["width"] - enemy["size"]), random.randint(1, config.game["height"] - enemy["size"])) enemy["dead"] = False playerSafeZone.center = player["rect"].center if not enemy['rect'].colliderect(playerSafeZone): container.append(enemy)
def place(self, r: Rect) -> Rect: """Place r into this texture. Raise NoFit if space could not be found. """ if not self.free: raise NoFit() try: r, block = min( self._solutions(r), key=self._fitness ) except ValueError as e: if e.args[0] != 'min() arg is an empty sequence': raise raise NoFit() from None r.topleft = block.topleft self._manage_free(Rect(r), block) return r
def AAfilledRoundedRect(surface, rect, color, radius=0.4): """ AAfilledRoundedRect(surface, rect, color, radius=0.4) surface : destination rect : rectangle color : rgb or rgba radius : 0 <= radius <= 1 """ rect = Rect(rect) color = pygame.Color(*color) alpha = color.a color.a = 0 pos = rect.topleft rect.topleft = 0, 0 rectangle = pygame.Surface(rect.size, SRCALPHA) circle = pygame.Surface([min(rect.size) * 3] * 2, SRCALPHA) pygame.draw.ellipse(circle, (0, 0, 0), circle.get_rect(), 0) circle = pygame.transform.smoothscale(circle, [int(min(rect.size) * radius)] * 2) radius = rectangle.blit(circle, (0, 0)) radius.bottomright = rect.bottomright rectangle.blit(circle, radius) radius.topright = rect.topright rectangle.blit(circle, radius) radius.bottomleft = rect.bottomleft rectangle.blit(circle, radius) rectangle.fill((0, 0, 0), rect.inflate(-radius.w, 0)) rectangle.fill((0, 0, 0), rect.inflate(0, -radius.h)) rectangle.fill(color, special_flags=BLEND_RGBA_MAX) rectangle.fill((255, 255, 255, alpha), special_flags=BLEND_RGBA_MIN) return surface.blit(rectangle, pos)
def update(self, game, dt: float, events: list): # Step through 15 sprite frames each second self.steps += dt frame = int(self.steps // (1.0 / 15)) # Find our center, if we have a player to focus on our_center = (0, 0) for key, entity in game.entities.items(): # Are they a player? if PlayerControl in entity and IngameObject in entity: # Are they us? if game.net.is_me(entity[PlayerControl].player_id): our_center = entity[IngameObject].position break invMapX = {"min": 0, "max": 0} invMapY = {"min": 0, "max": 0} # Draw tilemap for key, entity in game.entities.items(): if Map in entity and SpriteSheet in entity: spritesheet = entity[SpriteSheet] map = entity[Map] # minimum and maximum tile indexes coordinates possible min_x = math.floor( (our_center[0] - game.framework.dimensions[0] / 2) / spritesheet.tile_size) min_y = math.floor( (our_center[1] - game.framework.dimensions[1] / 2) / spritesheet.tile_size) max_x = math.floor( (our_center[0] + game.framework.dimensions[0] / 2) / spritesheet.tile_size) max_y = math.floor( (our_center[1] + game.framework.dimensions[1] / 2) / spritesheet.tile_size) for y in range(min_y, max_y + 1): for x in range(min_x, max_x + 1): if 0 <= x < map.width and 0 <= y < map.height: tile = map.grid[y][x] else: tile = 20 img_indexes = spritesheet.tiles[str(tile - 1)] if spritesheet.moving: img_index = img_indexes[frame % len(img_indexes)] else: img_index = img_indexes[0] image = self.get_image(spritesheet, img_index) rel_pos = (x * spritesheet.tile_size - our_center[0], y * spritesheet.tile_size - our_center[1]) screen_pos = (rel_pos[0] + game.framework.dimensions[0] / 2, rel_pos[1] + game.framework.dimensions[1] / 2) image = image.convert() alpha = math.sin(self.ticks / 1000) * 100 alpha = 100 - alpha if alpha > 255: alpha = 255 image.set_alpha(alpha) self.screen.blit(image, screen_pos) self.draw_particles(game, "below", our_center) #pygame.draw.rect(self.screen,(255,0,0),Rect(0,0,self.screen.get_width(), self.screen.get_height())) previousCollidables = [] # Render everything we can for key, entity in game.entities.items(): r = False # Don't check for items being picked up if CanPickUp in entity: if entity[CanPickUp].pickedUp: continue if Wieldable in entity: wieldable = entity[Wieldable] if not wieldable.wielded: entity[GameAction].action = "delete" continue #Check collisions for entity against all previously checked entities if IngameObject in entity and Collidable in entity: if entity[Collidable].canCollide: game.collisionSystem.checkCollisions( game, key, entity[Collidable], previousCollidables) previousCollidables.append((key, entity[Collidable])) if IngameObject in entity and Wieldable not in entity: # Where are they relative to us? pos = entity[IngameObject].position rel_pos = (pos[0] - our_center[0], pos[1] - our_center[1]) screen_pos = (rel_pos[0] + game.framework.dimensions[0] / 2, rel_pos[1] + game.framework.dimensions[1] / 2) if screen_pos[0] < 0 or screen_pos[0] > self.screen.get_width( ) or screen_pos[1] < 0 or screen_pos[ 1] > self.screen.get_height(): continue if IngameObject in entity and ParticleEmitter in entity: if entity[ParticleEmitter].colour == (-1, -1, -1): r = True new_particles = entity[ParticleEmitter].getParticles(entity) for new_part in new_particles: game.particles.add_particle(new_part) # Is this an entity we should draw? if IngameObject in entity and SpriteSheet in entity: # Where are they relative to us? pos = entity[IngameObject].position rel_pos = (pos[0] - our_center[0], pos[1] - our_center[1]) screen_pos = (rel_pos[0] + game.framework.dimensions[0] / 2, rel_pos[1] + game.framework.dimensions[1] / 2) spritesheet = entity[SpriteSheet] if Wieldable in entity: wieldable = entity[Wieldable] wielding_player = game.entities[wieldable.player_id] if wieldable.wielded: io = wielding_player[IngameObject] entity[IngameObject].position = (io.position[0] + 25, io.position[1]) dire = wielding_player[Directioned].direction direction = Directioned(direction='default') if dire == 'left': entity[IngameObject].position = (io.position[0] - 25, io.position[1]) direction = Directioned(direction='left') elif dire == 'right': entity[IngameObject].position = (io.position[0] + 25, io.position[1]) direction = Directioned(direction='right') elif dire == 'up': entity[IngameObject].position = (io.position[0] + 25, io.position[1] - 10) direction = Directioned(direction='up') elif dire == 'down': entity[IngameObject].position = (io.position[0] - 25, io.position[1] + 25) direction = Directioned(direction='down') if Directioned in entity: entity[Directioned] = direction img_indexes = spritesheet.tiles["default"] # Will they be facing a certain direction? if Directioned in entity: alts = spritesheet.tiles[entity[Directioned].direction] if alts != None: img_indexes = alts # Get the image relevent to how far through the animation we are if spritesheet.moving: img_index = img_indexes[frame % len(img_indexes)] else: img_index = img_indexes[spritesheet.default_tile] img = self.get_image(spritesheet, img_index) #Scale the image if img.get_size() != entity[IngameObject].size: img = pygame.transform.scale(img, entity[IngameObject].size) rect = Rect(screen_pos, entity[IngameObject].size) rect.center = screen_pos # Add a rectangle behind the item because the quantity is seen outside of the item if CanPickUp in entity: pygame.draw.circle(self.screen, (0, 255, 0), (int(rect.x + rect.width / 2), int(rect.y + rect.height / 2)), int(rect.width / 2)) # Draw the image, but only if it's on screen if not (rect.right < 0 or rect.left >= game.framework.dimensions[0] or rect.bottom < 0 or rect.top >= game.framework.dimensions[1]): self.screen.blit(img, rect) # If it is an item show the amount of items there are if CanPickUp in entity: if entity[CanPickUp].quantity > 1: rendered_text_qitem = self.font.render( str(entity[CanPickUp].quantity), False, (0, 0, 0)) self.screen.blit(rendered_text_qitem, rect) # Center health bar and nametag rect.x -= 30 # Checks if entity has an energy component if Energy in entity: # Energy bar wrapper energyBarThickness = 2 pygame.draw.rect(self.screen, (255, 255, 255), (rect.x, rect.y - 45, 100 + energyBarThickness * 2, 10), energyBarThickness) # Yellow energy bar if entity[Energy].value > 0: currentEnergyPos = (rect.x + energyBarThickness, rect.y - 45 + energyBarThickness, entity[Energy].value, 10 - energyBarThickness * 2) pygame.draw.rect(self.screen, (255, 255, 0), currentEnergyPos) # Checks if entity has a health component if Health in entity: # Health bar wrapper healthBarThickness = 2 pygame.draw.rect(self.screen, (255, 255, 255), (rect.x, rect.y - 30, 100 + healthBarThickness * 2, 10), healthBarThickness) # Red health bar if entity[Health].value > 0: healthValue = int(entity[Health].value / entity[Health].maxValue * 100) currentHealthPos = (rect.x + healthBarThickness, rect.y - 30 + healthBarThickness, healthValue, 10 - healthBarThickness * 2) pygame.draw.rect(self.screen, (255, 0, 0), currentHealthPos) if WaterBar in entity: if not entity[WaterBar].disabled: # Water bar wrapper waterBarThickness = 2 pygame.draw.rect(self.screen, (255, 255, 255), (rect.x, rect.y - 60, 100 + waterBarThickness * 2, 10), waterBarThickness) # Blue water bar if entity[Health].value > 0: currentWaterPos = (rect.x + waterBarThickness, rect.y - 60 + waterBarThickness, entity[WaterBar].value, 10 - waterBarThickness * 2) pygame.draw.rect(self.screen, (0, 0, 255), currentWaterPos) rect.y -= 15 # Does the entity have a name we can draw if Profile in entity: name = entity[Profile].name # Draw our name with our font in white rendered_text_surface = self.font.render( name, False, entity[Profile].colour if not r else Particle.get_random_colour()) # Move the nametag above the player rect.y -= 100 # Draw this rendered text we've made to the screen self.screen.blit(rendered_text_surface, rect) # Checks if it is a player if PlayerControl in entity: # Draw the inventory bar for us only if game.net.is_me(entity[PlayerControl].player_id): # Debugging if statement if Inventory in entity: inv = entity[Inventory] # Store the map borders entity[Inventory].mapMinX = invMapX["min"] entity[Inventory].mapMinY = invMapY["min"] entity[Inventory].mapMaxX = invMapX["max"] entity[Inventory].mapMaxY = invMapY["max"] # Inventory bar colours inventoryBackgroundColour = (183, 92, 5) slotBackgroundColour = (255, 159, 67) # Draw inventory bar inventoryPos = (inv.x, inv.y, inv.width, inv.height) pygame.draw.rect(self.screen, inventoryBackgroundColour, inventoryPos) invX = game.screen.get_width() / 2 - inv.width / 2 invY = game.screen.get_height( ) - inv.height - inv.slotOffset entity[Inventory].x, entity[ Inventory].y = invX, invY distanceBetweenSlots = inv.slotOffset + inv.slotSize # Draw slots slotIndex = 0 for x in range(int(invX + inv.slotOffset), int(invX + inv.width), distanceBetweenSlots): # The active slot has a different colour if slotIndex == inv.activeSlot: colour = (241, 196, 15) elif slotIndex == inv.hoverSlot: colour = (243, 156, 18) else: colour = slotBackgroundColour pygame.draw.rect(self.screen, colour, (x, invY + inv.slotOffset, inv.slotSize, inv.slotSize)) slotIndex += 1 # Drawing items in slots for slotIndex, data in entity[ Inventory].items.items(): if data: itemImgIndexes = data['sprite'].tiles[ 'default'] itemImgIndex = itemImgIndexes[ frame % len(itemImgIndexes)] # If it does, get its image itemImg = self.get_image( data['sprite'], itemImgIndex) itemW, itemH = (inv.slotSize - inv.slotOffset, inv.slotSize - inv.itemSlotOffset * 2) itemImg = pygame.transform.scale( itemImg, (itemW, itemH)) # The item is placed in the slot with a 3px offset itemRect = ( invX + (distanceBetweenSlots * slotIndex) + inv.itemSlotOffset, invY + inv.slotOffset + inv.itemSlotOffset, itemW, itemH) self.screen.blit(itemImg, itemRect) # Drawing text that shows how many items of this kind there are lItemRect = list(itemRect) lItemRect[1] += inv.slotOffset itemRect = tuple(lItemRect) rendered_text_qslot = self.font.render( str(data['quantity']), False, (0, 0, 128)) self.screen.blit(rendered_text_qslot, itemRect) slotIndex += 1 if Clock in entity: time_names = ("Dusk", "Dawn", "Morning", "Noon", "Afternoon", "Evening") rendered_text_surface = self.font.render( time_names[entity[Clock].minute], False, (255, 255, 255)) rect = rendered_text_surface.get_rect() rect.topleft = (10, 5) self.screen.blit(rendered_text_surface, rect) cycle = entity[Clock].cycle rendered_text_surface = self.font.render( str("Day"), False, (255, 255, 255)) rect = rendered_text_surface.get_rect() rect.topleft = (150, 5) self.screen.blit(rendered_text_surface, rect) cycle = entity[Clock].cycle rendered_text_surface = self.font.render( str(cycle), False, (255, 255, 255)) rect = rendered_text_surface.get_rect() rect.topleft = (215, 5) self.screen.blit(rendered_text_surface, rect) year = entity[Clock].year rendered_text_surface = self.font.render( str("Year"), False, (255, 255, 255)) rect = rendered_text_surface.get_rect() rect.topleft = (270, 5) self.screen.blit(rendered_text_surface, rect) year = entity[Clock].year rendered_text_surface = self.font.render( str(year), False, (255, 255, 255)) rect = rendered_text_surface.get_rect() rect.topleft = (340, 5) self.screen.blit(rendered_text_surface, rect) self.ticks = entity[Timed].time + ( entity[Clock].minute * 3600) + (entity[Clock].cycle * 21600) + (entity[Clock].year * 7776000) self.draw_particles(game, "above", our_center)
def blitPlayerShip(ps, location, screen, de): # NEED TO CHECK VISIBILITY ps.base_rect.center = location screen.blit(ps.base_image, ps.base_rect) ps.floor_location = (ps.floor_offset['x'] + ps.base_rect.x, ps.floor_offset['y'] + ps.base_rect.y) ps.floor_rect.topleft = ps.floor_location screen.blit(ps.floor_image, ps.floor_rect) tile_rect = Rect(0, 0, 35, 35) for tile in ps.tiles: tile_rect.topleft = tuple( map(sum, zip(tile, ps.floor_location, ps.tiles_offset))) screen.fill(FLOOR, tile_rect) #draw.rect not HW accelerated? Might be punishing rpi. draw.rect(screen, TILEBORDER, tile_rect, 1) for room in ps.rooms: room['location'] = tuple( map( sum, zip((room['x_pix'], room['y_pix']), ps.floor_location, ps.tiles_offset))) room['rect'].topleft = room['location'] for key, v in room.items(): if key == 'image': try: room['imagerect'].topleft = room['rect'].topleft screen.blit(room['roomimage'], room['imagerect']) except (KeyError): pass #print('image not found: ' + room['image']) break for key, v in room.items(): if key == 'system': room['system_image_rect'].center = room['rect'].center screen.blit(room['system_image'], room['system_image_rect']) #These should be masks break draw.rect(screen, BLACK, room['rect'], 3) for door in ps.doors: doorloc = tuple( map(sum, zip(door['location'], ps.floor_location, ps.tiles_offset))) if door['vertical']: door['rect'].centerx = doorloc[0] door['rect'].y = doorloc[1] else: door['rect'].x = doorloc[0] door['rect'].centery = doorloc[1] - 1 screen.blit(door['frames']['frames'][door['frame_num']], door['rect']) #room0_x = room0_x + ps_floor_location[0] #room0_y = room0_y + ps_floor_location[1] for crew_member in ps.crew: cm_loc = tuple( map( sum, zip(crew_member.location, ps.floor_location, ps.tiles_offset))) crew_member.rect.center = cm_loc screen.blit(crew_member.cur_anim['frames'][crew_member.cur_frame], crew_member.rect)
def get_fill_rect(self): fill_rect = Rect(self.rect) fill_rect.topleft = (0,0) # Topleft of the image fill_rect.top -= self.rect.height * self.get_percentage_health() return fill_rect
def __init__(self, size): temp_rect = Rect((0, 0), size) temp_rect.topleft = (0, size[1] + 50) self.collision_rect = temp_rect
def main(): init() running = True size = 1000, 1000 # these are the width and height of the simulation grid grid_x, grid_y = int(argv[1]), int(argv[2]) # this is how many pixels per cell in the simulation grid # We need to use the grid factor because the pygame window is not # the same width and height of the simulation grid size. Therefore, # we scale the pygame window down to fit the simulation size grid_factor_x, grid_factor_y = 1000 / grid_x, 1000 / grid_y screen = display.set_mode(size) # used to track either the setting of the topleft of a rectangle, # or the bottom right (if bottom right, the rectangle is completed) num_clicks_patch = 0 num_clicks_patch_temp = 0 num_clicks_exit = 0 num_clicks_exit_temp = 0 # the position of the mouse when the left or right mouse button is clicked mouse_pos_patch = () mouse_pos_exit = () # the position of the patch's top left corner in pixels patch_tl = () # the position of the patch's bottom right corner in pixels patch_br = () # the position of the exits's top left corner in pixels exit_tl = () # the position of the exits's bottom right corner in pixels exit_br = () patch_temp = Rect(-1, -1, 0, 0) exit_temp = Rect(-1, -1, 0, 0) # a list of patches as pygame rectangles that are being displayed # on screen patch_list = [] # a list of patches removed from the screen. The user can recall these # with a keybind patch_discard_list = [] # a list of exits as pygame rectangles that are being displayed # on screen exit_list = [] # a list of exits removed from the screen. The user can recall these # with a keybind exit_discard_list = [] # a list of 1 pixel-wide pygame rectangles that are drawn to the screen # to create grid lines gridline_list = [] # populate `gridline_list` gridline_list = populate_gridline_list(gridline_list, grid_x, grid_y, grid_factor_x, grid_factor_y) # main game loop will run until the user clicks the exit button in the top right corner (Windows) while running: # event loop for pygame_event in event.get(): # if the user hits the window's exit button, stop the app if pygame_event.type == QUIT: running = False # if the user hits the mouse button in the app window if pygame_event.type == MOUSEBUTTONDOWN and mouse.get_focused(): # return a list of booleans for the three major buttons on a mouse: # left mouse, right mouse, and middle mouse. If an element is True, # the corresponding button is pressed mouse_depressed = mouse.get_pressed(num_buttons=3) # if the user clicks ONLY the left mouse button, they are creating # a patch. if mouse_depressed[0] and not mouse_depressed[2]: # get the mouse's position with respect to the window mouse_pos_patch = mouse.get_pos() num_clicks_patch += 1 num_clicks_patch_temp = num_clicks_patch # if the user clicks ONLY the right mouse button, they are creating # an exit. if mouse_depressed[2] and not mouse_depressed[0]: mouse_pos_exit = mouse.get_pos() num_clicks_exit += 1 num_clicks_exit_temp = num_clicks_exit if pygame_event.type == KEYDOWN and key.get_focused(): # return a list of booleans for all the keys on the keyboard # If an element is True, the corresponding key is pressed keys_depressed = key.get_pressed() # if the user presses CTRL+Z, remove the previously added patches # from most recent to least recent if keys_depressed[K_LCTRL] and keys_depressed[ K_z] and not keys_depressed[K_LSHIFT]: # if the user has begun to create a new patch, they can # cancel it by hitting CTRL+Z. Otherwise, CTRL+Z removes a # patch that has already been drawn if patch_list and num_clicks_patch == 0: patch_discard_list.append(patch_list.pop()) print("Number of patches:", len(patch_list)) else: patch_tl = () num_clicks_patch = 0 num_clicks_patch_temp = 0 print("Undid patch topleft!") # if the user pressed CTRL+SHIFT+Z, remove the previously added exits # from most recent to least recent elif keys_depressed[K_LSHIFT] and keys_depressed[ K_LCTRL] and keys_depressed[K_z]: # if the user has begun to create a new exit, they can # cancel it by hitting CTRL+SHIFT+Z. Otherwise, CTRL+SHIFT+Z removes a # exit that has already been drawn if exit_list and num_clicks_exit == 0: exit_discard_list.append(exit_list.pop()) print("Number of exits:", len(exit_list)) else: exit_tl = () num_clicks_exit = 0 num_clicks_exit_temp = 0 print("Undid exit topleft!") # if the user presses CTRL+Y, restore the previously removed patch # from the discard patch list from most recent to least recent if keys_depressed[K_LCTRL] and keys_depressed[ K_y] and not keys_depressed[K_LSHIFT]: # the user can only restore a deleted patch if they haven't # started drawing another one. Easy fix for this: press CTRL+Z # to undo the topleft of the already-begun patch, then do CTRL+Y if patch_discard_list and num_clicks_patch == 0: patch_list.append(patch_discard_list.pop()) print("Number of patches:", len(patch_list)) # if the user pressed CTRL+SHIFT+Y, restore the previously removed exit # from the discard exit list from most recent to least recent elif keys_depressed[K_LSHIFT] and keys_depressed[ K_LCTRL] and keys_depressed[K_y]: # the user can only restore a deleted exit if they haven't # started drawing another one. Easy fix for this: press CTRL+SHIFT+Z # to undo the topleft of the already-begun exit, then do CTRL+SHIFT+Y if exit_discard_list and num_clicks_exit == 0: exit_list.append(exit_discard_list.pop()) print("Number of exits:", len(exit_list)) # if the user has clicked the mouse once, store the mouse position # as the top left of the rectangle to be drawn if num_clicks_patch_temp == 1: patch_tl = mouse_pos_patch num_clicks_patch_temp = 0 patch_temp.topleft = patch_tl print("Patch topleft set at", patch_tl) # if the user has clicked the mouse for the second time, elif num_clicks_patch_temp == 2: patch_br = mouse_pos_patch patch_list.append( Rect(patch_tl[0], patch_tl[1], patch_br[0] - patch_tl[0], patch_br[1] - patch_tl[1])) num_clicks_patch = 0 num_clicks_patch_temp = 0 print("Number of patches:", len(patch_list)) # if the user has clicked the mouse once, store the mouse position # as the top left of the rectangle to be drawn if num_clicks_exit_temp == 1: exit_tl = mouse_pos_exit num_clicks_exit_temp = 0 exit_temp.topleft = exit_tl print("Exit topleft set at", exit_tl) # if the user has clicked the mouse for the second time, elif num_clicks_exit_temp == 2: exit_br = mouse_pos_exit exit_list.append( Rect(exit_tl[0], exit_tl[1], exit_br[0] - exit_tl[0], exit_br[1] - exit_tl[1])) num_clicks_exit = 0 num_clicks_exit_temp = 0 print("Number of exits:", len(exit_list)) # first blacken the screen screen.fill("black") # draw the grid lines for gridline in gridline_list: draw.rect(screen, "blue", gridline) # draw the patches on top of the grid lines for patch in patch_list: patch.normalize() draw.rect(screen, "white", patch, width=1) # then draw the exits over the grid lines and patches for exit in exit_list: exit.normalize() draw.rect(screen, "red", exit) # if the user has clicked to begin drawing either a patch # or an exit, display the box growing and shrinking as they # move their mouse so they can see what the patch/exit will look # like when it's drawn if num_clicks_patch == 1: patch_temp.size = tuple( map(sub, mouse.get_pos(), patch_temp.topleft)) # we'll use `patch_temp_copy` to display a proper rectangle if the user defines # `patch_temp`'s topleft and then moves the mouse above or farther left than # that point. Doing so creates a negative width/height in the rectangle which # does not draw to the screen without glitching. Normalizing the rectangle # removes the negative width/height and reassigns the topleft point in the process. # Without using a copy of `patch_temp`, its top left would change every game loop, # and the rectangle would not change with mouse movement correctly. If this # description doesn't make sense, just trust me. If you're the kind of person # who likes to define the bottom right corner first, this code lets you see # that normalized (looks like a rectangle, not a cross road) rectangle change # size as your mouse moves around :D patch_temp_copy = Rect(-1, -1, 0, 0) # I have to define `patch_temp_copy` like this instead of `patch_temp_copy = patch_temp` # since that would just make `patch_temp_copy` an alias of `patch_temp` patch_temp_copy.topleft = patch_temp.topleft patch_temp_copy.size = patch_temp.size patch_temp_copy.normalize() draw.rect(screen, "white", patch_temp_copy, width=1) print(patch_temp_copy.size, " ", end='\r') if num_clicks_exit == 1: # same theory as with `patch_temp_copy` so I'm not gonna repeat it... exit_temp.size = tuple(map(sub, mouse.get_pos(), exit_temp.topleft)) exit_temp_copy = Rect(-1, -1, 0, 0) exit_temp_copy.topleft = exit_temp.topleft exit_temp_copy.size = exit_temp.size exit_temp_copy.normalize() draw.rect(screen, "red", exit_temp_copy) print(exit_temp_copy.size, " ", end='\r') # this updates the contents of the surface display.flip() # if the user has clicked the exit button, ask them if they either want to create # the terrain or completely exit the editor. Give them a second chance if they # want to quit. That way they don't accidentally lose their progress. if not running: response = input( "Would you like to create the terrain with this model? (y/n) ") if response == 'y': # create the terrain grid from the rectangles drawn in the pygame window terrain_grid = create_terrain_grid(patch_list, exit_list, grid_x, grid_y, grid_factor_x, grid_factor_y) # draw converted rectangles to the .txt file output_to_file(grid_x, grid_y, terrain_grid) # save the pygame screen as a .png to use as the background image of the simulation # visualization .gif save_screen_as_bkgr(screen) # update the patches document in the `params.json` file update_patches(patch_list, grid_factor_x, grid_factor_y) elif response == 'n': second_response = input("Are you sure? (Y/N) ") if second_response == "Y": print("Quitting terrain editor.") elif second_response == "N": running = True print("Continuing terrain editor.") else: running = True print( "Invalid response ('Y' or 'N' only, please). Continuing terrain editor." ) else: running = True print( "Invalid response ('y' or 'n' only, please). Continuing terrain editor." ) quit()