class Player: def __init__(self, x, y, width, height, img, rotation): self.x = x self.y = y self.vel = 15 self.width = width self.height = height self.img = img self.rotation = rotation def draw(self, win): # think it is wrong here img = pygame.image.load(self.img) img = pygame.transform.scale(img, (self.width, self.height)) img = pygame.transform.rotate(img, self.rotation) win.blit(img, (self.x, self.y)) # skal ikke have win def move(self, win): self.l = Laser(self.x, self.y) keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: self.x -= self.vel if keys[pygame.K_RIGHT]: self.x += self.vel if keys[pygame.K_SPACE]: print('presesd space') self.l.draw(win) # if keys[pygame.K_UP]: # print('Key_UP') # l.draw(win) self.update(win) def update(self, win): img = pygame.image.load(self.img) img = pygame.transform.scale(img, (self.width, self.height)) img = pygame.transform.rotate(img, self.rotation) win.blit(img, (self.x, self.y))
class Ship(): """The ship class encapsulates absolute coordinates (x, y) of the upper-left corner, image, bitmask for collision detection, vertical speed, moving and shooting status, ship status itself, laser and jets objects.""" def __init__(self, scr, view_point, explosions): """Input parameters: scr - Surface for drawing; view_point - ViewPoint class instance; explosions - Explosions class instance for ship destroying.""" self.scr = scr self.view_pt = view_point self.explosions = explosions self.image = pygame.image.load(f'img/{SHIP_FILE}') self.mask = pygame.mask.from_surface(self.image) self.rect = self.image.get_rect() self.x = -(self.rect.width / 2) self.y = self.rect.height self._update_rect() self.laser = Laser(self.scr, self.view_pt) self._update_laser_pos() self.jet_image = pygame.image.load(f'img/{JET_FILE}') self.jets = { 'left': AnimatedSprite(self.jet_image, self.scr, self.view_pt, JET_FRAME_COLS, JET_FRAME_ROWS, reverse=True), 'right': AnimatedSprite(self.jet_image, self.scr, self.view_pt, JET_FRAME_COLS, JET_FRAME_ROWS, reverse=True), } self._update_jets_pos() self.speed = 0 self._reset_user_control() self.status = STATUS_NORMAL self._reset_progress() def _reset_user_control(self): self.moving_up = False self.moving_down = False self.moving_left = False self.moving_right = False self.shooting = False def _reset_progress(self): # Ship destructive explosion is continuous process which status # keeps self.progress attribute self.progress = 0 # Keeps key points of the progress where explosions must be created self.key_frames = [] def _update_laser_pos(self): self.laser.set_origin(self.x + (self.rect.width / 2) + LASER_OFFSET_X, self.y - LASER_OFFSET_Y) def _update_rect(self): self.rect.x = self.view_pt.x_to_scr(self.x) self.rect.y = self.view_pt.y_to_scr(self.y) def get_center(self): """Returns point (x, y) with absolute ship center coordinates.""" return (self.x + self.rect.width / 2, self.y - self.rect.height / 2) def update(self): """Call before any collision detection, drawing ship, etc.""" full_speed = self.speed + self.acceleration * (self.y**1.2) if self.status in (STATUS_NORMAL, STATUS_RESTORING, STATUS_AUTO): self.y += full_speed if self.status == STATUS_NORMAL and self.shooting: self.laser.shoot() if self.status == STATUS_NORMAL or self.status == STATUS_RESTORING: if self.moving_left: self.x -= SHIP_MOVEMENT if self.moving_right: self.x += SHIP_MOVEMENT if self.acceleration == 0: # Don't move vertically if acceleration > 0 if self.moving_up: self.y += SHIP_MOVEMENT if self.moving_down: self.y -= SHIP_MOVEMENT if self.status == STATUS_EXPLODING: self.progress += 1 if self.progress in self.key_frames: self._add_explosion() if self.progress >= PROGRESS_MAX: self._add_explosion( self.rect.center, explosion_ind=explosions.DOUBLE_EXPLOSION_IND) self._reset_progress() self.status = STATUS_INACTIVE if self.status == STATUS_RESTORING: self.progress += 1 if self.progress >= PROGRESS_MAX: self._reset_progress() self.status = STATUS_NORMAL self.view_pt.set_speed(full_speed) self.view_pt.set_trace_point(self.get_center()) self._update_rect() self._update_laser_pos() self.laser.update() self._update_jets_pos() def set_speed(self, speed): self.speed = speed def set_acceleration(self, acceleration): self.acceleration = acceleration def _update_jets_pos(self): bottom = self.y - self.rect.height centerx = self.x + (self.rect.width - self.jets['left'].rect.width) / 2 self.jets['left'].x = centerx - JET_OFFSET_X self.jets['right'].x = centerx + JET_OFFSET_X self.jets['left'].y = bottom - JET_OFFSET_Y self.jets['right'].y = bottom - JET_OFFSET_Y self.jets['left'].update() self.jets['right'].update() def is_visible(self): """This method helps with implementing 'blinking' effect.""" if self.status == STATUS_INACTIVE: return False elif self.status == STATUS_EXPLODING: return (self.progress % int(self.progress / 10 + 1)) == 0 elif self.status == STATUS_RESTORING: return ((self.progress // BLINKING_GAP) % 2) == 0 else: #At this point self.status in [STATUS_NORMAL, STATUS_AUTO] return True def draw(self): if self.is_visible(): self.laser.draw() for jet in self.jets.values(): jet.draw() jet.next_frame() self.scr.blit(self.image, self.rect) def _add_explosion(self, point=None, explosion_ind=None): """If specified - point parameter contains screen coordinates of explosion.""" if point: x = self.view_pt.scr_to_x(point[0]) y = self.view_pt.scr_to_y(point[1]) else: x = self.view_pt.scr_to_x(randint(self.rect.left, self.rect.right)) y = self.view_pt.scr_to_y(randint(self.rect.top, self.rect.bottom)) if explosion_ind: self.explosions.add(x, y, explosion_ind) else: self.explosions.add(x, y) def explode(self, collide_point=None): """Starts ship explosion process. If specified collide_point has absolute coordinates of destructive collision.""" if self.status != STATUS_NORMAL: return False get_sound_box().play_multi_explosion() self.status = STATUS_EXPLODING self._reset_progress() self._add_explosion() if collide_point: self.explosions.add(collide_point[0], collide_point[1], explosions.SMALL_EXPLOSION_IND) for i in range(EXPLOSIONS_MAX - 1): self.key_frames.append(randint(1, PROGRESS_MAX - 1)) return True def set_autopilot(self): """Imperatively disables player control of the ship. The only way to return the control is to call restore().""" self.status = STATUS_AUTO def set_center(self, center_x, center_y): """Sets the absolute center coordinates of the ship.""" self.x = center_x - (self.rect.width / 2) self.y = center_y + (self.rect.height / 2) self._update_rect() def restore(self, center_point=None, reset_control=False): """Restores ship after destruction or just assigning special 'restoring' status (blinking and temporary invincibility). Input parameters: center_point - new absolute coordinates of the ship (no changing position if None); reset_control - if True then stops moving left/right/up/down and shooting; useful to avoid glitch when game level starts.""" if self.status not in [STATUS_NORMAL, STATUS_INACTIVE, STATUS_AUTO]: return False self.status = STATUS_RESTORING self._reset_progress() if reset_control: self._reset_user_control() if center_point: self.set_center(center_point[0], center_point[1]) return True
class RocketShip(Player): def __init__(self, FPS, x, y, sprite, spriteLaser): Player.__init__(self, x, y, sprite) self.soundLaser = pygame.mixer.Sound(os.path.join("sounds", "laser.ogg")) self.soundLaser.set_volume(0.5) self.leftCTRL_UP = False # the player can only have 1 laser active, so one 1 is every created. self.laser = Laser(self.x, self.y - self.spriteCentre.height / 2, spriteLaser) self.score = 0 # player will start game alive self.alive = True self.lives = 2 # every time a screen is cleared this will be incremented. # this effects how many galaxians can dive at once and the time delay between galaxians launching. self.level = 0 # wait 4 seconds to reinitialise the player or decide the game is over self.waitTime = FPS * 4 # this timer is only decremented once the player is NOT alive self.waitTimer = self.waitTime self.spriteLife = pygame.image.load(os.path.join("images", "rocketShipLife.png")).convert_alpha() self.spriteLifeRect = self.spriteLife.get_rect() def move(self, gal, screenWidth): # updates the sprite and hit rectangle Player.move(self) if self.alive: userInput = pygame.key.get_pressed() if userInput[pygame.K_LEFT] and self.alive: self.x -= 3 if userInput[pygame.K_RIGHT] and self.alive: self.x += 3 if userInput[pygame.K_LCTRL] and self.leftCTRL_UP and not self.laser.active: self.laser.active = True self.soundLaser.play() # stop the player moving off the screen self.x = limits(self.x, 0, screenWidth, self.spriteCentre.width / 2) # prevents auto fire self.leftCTRL_UP = not userInput[pygame.K_LCTRL] # update the laser, regardless if it is active or not self.laser.move(self.x, gal) # game is not over, return false return False else: self.sprite = self.spriteBlank if self.laser.active: self.laser.move(self.x, gal) else: # just keep it out of sight for now self.laser.sprite = self.laser.spriteBlank state = States() # wait until all galaxians are back in formation before setting the player back up for f in range(len(gal)): if not gal[f].state == state.FORMATION: # the players lives might be < 0, but return false until all galaxians are back in formation return False if self.waitTimer > 0: self.waitTimer -= 1 if self.waitTimer == 0: self.lives -= 1 if self.lives >= 0: self.alive = True self.x = screenWidth / 2 self.laser.x = self.x self.waitTimer = self.waitTime self.sprite = self.spriteStart self.laser.sprite = self.laser.spriteStart # player still has lives return False else: # game over now! return True def draw(self, window, screenHeight): Player.draw(self, window) self.laser.draw(window) for f in range(self.lives): window.blit(self.spriteLife, (10 + f * (self.spriteLifeRect.width + 5), screenHeight - self.spriteLifeRect.height))