class Scene(object): def __init__(self, screen): """Create the scene's objects.""" self.screen = screen self.running = True # Groups for sprites self.players = Group() self.enemies = Group() self.chars = Group() self.weapons = Group() self.projs = Group() self.all = LayeredDirty() # Room self.room = Room(self) self.all.add(self.room) # Weapons self.weapon_list = [ LaserGun(self), Bow(self) ] for weapon in self.weapon_list: self.weapons.add(weapon) # Players self.player = Player(self) self.players.add(self.player) self.all.add(self.players) # Enemies enemy_list = [ Human, Glork ] for enemy in range(0, randint(50,100)): self.enemies.add(choice(enemy_list)(self)) self.all.add(self.enemies) # Characters self.chars.add([self.players, self.enemies]) # Layers self.all = LayeredDirty([ self.room, self.enemies, self.players, self.weapons, self.projs ]) def draw(self): """Draw all of the objects to the screen.""" # Update all scene layers to the screen. self.all.update() self.dirty_rects = self.all.draw(self.screen) display.update(self.dirty_rects)
class CatUniScene(Scene): def __init__(self, *args, **kwargs): Scene.__init__(self, *args, **kwargs) (width, height) = (1920 // 2, 1080 // 2) self.width, self.height = width, height # Loading screen should always be a fallback active scene self.active = False self.first_render = True self.myfont = pygame.font.SysFont("monospace", 20) self.background = gfx('background.png', convert=True) # self.cat_unicycle = gfx('cat_unicycle.png').convert_alpha() # self.fish = gfx('fish.png').convert_alpha() # self.foot = gfx('foot.png').convert_alpha() # self.foot_part = gfx('foot_part.png').convert_alpha() # self.shark = gfx('shark.png').convert_alpha() sfx('cat_jump.ogg') sfx('eatfish.ogg') sfx('splash.ogg') sfx('cat_crash.ogg') self.meow_names = [ 'cat_meow01.ogg', 'cat_meow02.ogg', 'cat_meow03.ogg' ] self.last_meow = None self.touching_ground = True self.jumping = False self.jumping_time = 0 self.jump_key = None for meow_name in self.meow_names: sfx(meow_name) self.boing_names = ['boing1.ogg', 'boing2.ogg', 'boing3.ogg'] for boing_name in self.boing_names: sfx(boing_name) #cat variables self.cat_wire_height = height - 100 self.cat_location = [width / 2, height - 100] self.cat_speed = [0, 0] self.cat_speed_max = 8 self.cat_fall_speed_max = 16 self.cat_roll_speed = .01 self.cat_angle = 0 self.cat_angular_vel = 0 self.cat_head_location = [ int(self.cat_location[0] + 100 * math.cos(self.cat_angle - math.pi / 2)), int(self.cat_location[1] + 100 * math.sin(self.cat_angle - math.pi / 2)), ] self.people_mad = False self.people_mad_duration = 3000 #ms self.people_mad_current_time = 0 self.next_notfish = 0 self.notfish_time = 0 self.last_joy_right_tilt = 0 self.last_joy_left_tilt = 0 self.left_pressed = False self.right_pressed = False self.score = 0 #timing self.dt_scaled = 0 self.total_time = 0 #elephant and shark classes self.elephant = Elephant(self) self.shark_active = False #is the shark enabled yet self.elephant_active = False self.cat = Cat(self) self.score_text = Score(self) self.deadzones = [] # self.deadzones = [ # DeadZone( # [ # [0, height - 100], # [0.1 * width, height - 100], # [0.1 * width, height], # [0, height], # ], # ), # DeadZone( # [ # [0.9 * width, height - 100], # [width, height - 100], # [width, height], # [0.9 * width, height], # ], # ), # ] self.init_sprites() # lists of things to catch by [posx, posy, velx, vely] # self.fish = [[0, height / 2, 10, -5]] self.fish = LayeredDirtyAppend() self.fish.extend([Fish(self.allsprites, 0, height / 2, 10, -5)]) self.not_fish = LayeredDirtyAppend() self.unicycle_sound = sfx('unicycle.ogg', play=True, loops=-1, fadein=500) self.reset_meow() #difficulty varibles self.number_of_not_fish = 0 def reset_meow(self): self.next_meow = random.uniform(5000, 10000) def meow(self): # Play a meow sound, but not the same one twice in a row meow_names = self.meow_names[:] if self.last_meow in self.meow_names: meow_names.remove(self.last_meow) self.last_meow = random.choice(meow_names) sfx(self.last_meow, play=1) self.reset_meow() def init_sprites(self): """temp, this will go in the init. """ sprite_list = [self.elephant, self.cat, self.score_text] sprite_list += self.deadzones self.allsprites = LayeredDirty(sprite_list, _time_threshold=1000 / 10.0) scene = self self.shark = Shark(self.allsprites, scene, self.width, self.height) self.allsprites.add(self.shark) self.allsprites.clear(self.screen, self.background) #what to do when you die, reset the level def reset_on_death(self): self.cat_location = [self.width / 2, self.height - 100] self.cat_speed = [0, 0] self.cat_angle = 0 self.cat_angular_vel = 0 self.score = 0 self.total_time = 0 self.elephant.last_animation = 0 self.elephant.state = 0 self.elephant.just_happened = None self.elephant.dirty = 1 self.elephant_active = False self.elephant.animate(self.total_time) #make the shark leave self.shark_active = False self.shark.last_animation = 0 self.shark.dirty = True if self.shark.get_state() in ('aiming', 'fire laser'): self.shark.just_happenend = None self.shark.set_state('leaving') self.shark.applaud = False else: self.shark.just_happenend = None self.shark.set_state('offscreen') self.shark.animate(self.total_time) sfx('shark_appear.ogg', fadeout=1000) if self.shark.lazer: self.shark.lazer.kill() #periodically increase the difficulty def increase_difficulty(self): self.number_of_not_fish = 0 if self.score > 3: self.number_of_not_fish = 1 if self.score > 9: self.number_of_not_fish = 1 if self.score > 15: self.number_of_not_fish = 2 if self.score > 19: self.number_of_not_fish = 1 if self.score > 25: self.number_of_not_fish = 2 if self.score > 35: self.number_of_not_fish = 3 if self.score >= 50: self.number_of_not_fish = int((self.score - 20) / 10) #TODO: to make it easier to test. # if self.score >= 15: # self.shark_active = True if self.score >= 10: self.shark_active = True #TODO: to make it easier to test. # Elephant doesn't work yet, so let's not use it # if self.score >= 20: # self.elephant_active = True def annoy_crowd(self): self.people_mad = True self.people_mad_current_time = 0 def render_sprites(self): rects = [] self.allsprites.update() rects.extend(self.allsprites.draw(self.screen)) return rects def render(self): rects = [] if self.first_render: self.first_render = False rects.append(self.screen.get_rect()) rects.extend(self.render_sprites()) return rects def tick(self, dt): self.increase_difficulty() self.cat.animate(dt) self.total_time += dt #keep track of the total number of ms passed during the game dt_scaled = dt / 17 self.dt_scaled = dt_scaled width, height = self.width, self.height ##cat physics self.cat_angular_vel *= 0.9**dt_scaled #max(0.9/(max(0.1,dt_scaled)),0.999) #make the cat slide in the direction it's rotated self.cat_speed[0] += math.sin( self.cat_angle) * (dt_scaled * self.cat_roll_speed) # add gravity self.cat_speed[1] = min(self.cat_speed[1] + (1 * dt_scaled), self.cat_fall_speed_max) self.unicycle_sound.set_volume( abs(self.cat_speed[0] / self.cat_speed_max)) # accelerate the cat left or right if self.right_pressed: self.cat_speed[0] = min(self.cat_speed[0] + 0.3 * dt_scaled, self.cat_speed_max) self.cat_angle -= 0.003 * dt_scaled if self.left_pressed: self.cat_speed[0] = max(self.cat_speed[0] - 0.3 * dt_scaled, -self.cat_speed_max) self.cat_angle += 0.003 * dt_scaled # make the cat fall angle_sign = 1 if self.cat_angle > 0 else -1 self.cat_angular_vel += 0.0002 * angle_sign * dt_scaled self.cat_angle += self.cat_angular_vel * dt_scaled if (self.cat_angle > math.pi / 2 or self.cat_angle < -math.pi / 2 ) and self.cat_location[1] > height - 160: sfx('cat_crash.ogg', play=1) self.reset_on_death() # move cat self.cat_location[0] += self.cat_speed[0] * dt_scaled self.cat_location[1] += self.cat_speed[1] * dt_scaled if self.cat_location[1] > self.cat_wire_height and self.cat_location[ 0] > 0.25 * width: self.touching_ground = True self.cat_location[1] = self.cat_wire_height self.cat_speed[1] = 0 else: self.touching_ground = False if self.cat_location[1] > height: sfx('splash.ogg', play=1) self.meow() self.reset_on_death() if self.cat_location[0] > width: self.cat_location[0] = width if self.cat_angle > 0: self.cat_angle *= 0.7 self.cat_head_location = [ int(self.cat_location[0] + 100 * math.cos(self.cat_angle - math.pi / 2)), int(self.cat_location[1] + 100 * math.sin(self.cat_angle - math.pi / 2)), ] # check for out of bounds if self.cat_location[0] > 0.98 * width and self.cat_location[ 1] > self.cat_wire_height - 30: #bump the cat back in self.meow() sfx(random.choice(self.boing_names), play=True) self.cat_angular_vel -= 0.01 * dt_scaled self.cat_speed[0] = -5 self.cat_speed[1] = -20 #self.reset_on_death() if self.cat_location[0] < 0.25 * width and self.cat_location[ 1] > self.cat_wire_height - 30: pass #check for collision with the elephant stomp if self.elephant_active: self.elephant.animate(self.total_time) self.elephant.collide(self, width, height, self.cat_head_location) if self.shark_active or self.shark.states[ self.shark.state] == 'leaving': self.shark.animate(self.total_time) self.shark.collide(self, width, height, self.cat_location) #jumping physics if self.jumping: self.cat_speed[1] -= dt * ( (CAT_MAX_JUMPING_TIME - self.jumping_time) / CAT_MAX_JUMPING_TIME) * CAT_JUMP_SPEED self.jumping_time += dt if self.jumping_time >= CAT_MAX_JUMPING_TIME: self.jumping = False ##meow timing if self.next_meow <= 0: self.meow() self.next_meow -= dt ##angry people (increased throwing of not-fish) if self.people_mad: self.people_mad_current_time += dt self.notfish_time += dt if self.notfish_time >= self.next_notfish: self.next_notfish = random.randint(100, 400) self.notfish_time = 0 self.SpawnNotFish() if self.people_mad_current_time >= self.people_mad_duration: self.people_mad = False ##object physics # move fish and not fish for f in reversed(self.fish.sprites()): f.pos[0] += f.velocity[0] * dt_scaled # speed of the throw f.velocity[1] += 0.2 * dt_scaled # gravity f.pos[1] += f.velocity[1] * dt_scaled # y velocity # check out of bounds if f.pos[1] > height: self.fish.remove(f) f.kill() for f in reversed(self.not_fish.sprites()): f.pos[0] += f.velocity[0] * dt_scaled # speed of the throw f.velocity[1] += 0.2 * dt_scaled # gravity f.pos[1] += f.velocity[1] * dt_scaled # y velocity # check out of bounds if f.pos[1] > height: self.not_fish.remove(f) f.kill() # check collision with the cat for f in reversed(self.fish.sprites()): if distance([f.rect[0], f.rect[1]], self.cat_head_location) < 100: self.score += 1 self.fish.remove(f) sfx('eatfish.ogg', play=1) f.kill() for f in reversed(self.not_fish.sprites()): if distance([f.rect[0], f.rect[1]], self.cat_head_location) < 50: self.not_fish.remove(f) f.kill() self.angle_to_not_fish = (math.atan2( self.cat_head_location[1] - f.rect[1], self.cat_head_location[0] - f.rect[0], ) - math.pi / 2) side = 1 if self.angle_to_not_fish < 0 else -1 self.cat_angular_vel += side * random.uniform(0.08, 0.15) sfx(random.choice(self.boing_names), play=True) # refresh lists while len(self.fish) < 1 and not self.people_mad: # choose a side of the screen if random.choice([0, 1]) == 0: self.fish.append( Fish( self.allsprites, 0, height / 2, #random.randint(0, height / 2), random.randint(3, 7), -random.randint(5, 12), )) else: self.fish.append( Fish( self.allsprites, width, height / 2, #random.randint(0, height / 2), -random.randint(3, 7), -random.randint(5, 12), )) while len(self.not_fish) < self.number_of_not_fish: self.SpawnNotFish() def SpawnNotFish(self): # choose a side of the screen velocity_multiplier = 1 x_pos = 0 if random.randint(0, 1): velocity_multiplier *= -1 x_pos = self.width self.not_fish.append( NotFish( self.allsprites, x_pos, self.height / 2, random.randint(3, 7) * velocity_multiplier, -random.randint(5, 12), )) def start_jump(self, key): self.jump_key = key if self.touching_ground and not self.jumping: self.jumping = True self.jumping_time = 0 self.cat_speed[1] -= 12.5 sfx('cat_jump.ogg', play=1) def stop_jump(self): self.jumping = False sfx('cat_jump.ogg', fadeout=50) def tilt_left(self): self.cat_angular_vel -= random.uniform(0.01 * math.pi, 0.03 * math.pi) def tilt_right(self): self.cat_angular_vel += random.uniform(0.01 * math.pi, 0.03 * math.pi) def event(self, event): if event.type == KEYDOWN: if event.key == K_RIGHT: self.right_pressed = True elif event.key == K_LEFT: self.left_pressed = True elif event.key == K_a: self.tilt_left() elif event.key == K_d: self.tilt_right() elif event.key in (K_UP, K_SPACE): self.start_jump(event.key) elif event.type == KEYUP: if event.key == self.jump_key: self.stop_jump() elif event.key == K_RIGHT: self.right_pressed = False elif event.key == K_LEFT: self.left_pressed = False if event.type == JOYBUTTONDOWN: if event.button in JOY_JUMP_BUTTONS: self.start_jump("JOY" + str(event.button)) if event.button in JOY_LEFT_BUTTONS: self.tilt_left() if event.button in JOY_RIGHT_BUTTONS: self.tilt_right() if event.type == JOYBUTTONUP: if "JOY" + str(event.button) == self.jump_key: self.stop_jump() if event.type == JOYAXISMOTION: if event.axis == 0: if event.value >= JOY_SENSE: self.right_pressed = True self.left_pressed = False elif event.value <= -JOY_SENSE: self.right_pressed = False self.left_pressed = True else: self.right_pressed = False self.left_pressed = False if event.axis == JOY_TILT_RIGHT_AXIS: if self.last_joy_right_tilt < JOY_SENSE and event.value >= JOY_SENSE: self.tilt_right() self.last_joy_right_tilt = event.value if event.axis == JOY_TILT_LEFT_AXIS: if self.last_joy_left_tilt < JOY_SENSE and event.value >= JOY_SENSE: self.tilt_left() self.last_joy_left_tilt = event.value
class CatUniScene(Scene): def __init__(self, *args, **kwargs): Scene.__init__(self, *args, **kwargs) (width, height) = (1920//2, 1080//2) self.width, self.height = width, height # Loading screen should always be a fallback active scene self.active = False self.first_render = True self.myfont = pygame.font.SysFont("monospace", 20) self.background = gfx('background.png').convert() # self.cat_unicycle = gfx('cat_unicycle.png').convert_alpha() # self.fish = gfx('fish.png').convert_alpha() # self.foot = gfx('foot.png').convert_alpha() # self.foot_part = gfx('foot_part.png').convert_alpha() # self.shark = gfx('shark.png').convert_alpha() sfx('cat_jump.ogg') sfx('eatfish.ogg') #cat variables self.cat_wire_height = height - 100 self.cat_location = [width / 2, height - 100] self.cat_speed = [0, 0] self.cat_speed_max = 8 self.cat_fall_speed_max = 16 self.cat_angle = 0 self.cat_angular_vel = 0 self.cat_head_location = [ int(self.cat_location[0] + 100 * math.cos(self.cat_angle - math.pi / 2)), int(self.cat_location[1] + 100 * math.sin(self.cat_angle - math.pi / 2)), ] self.left_pressed = False self.right_pressed = False self.score = 0 #timing self.dt_scaled = 0 self.total_time = 0 #elephant and shark classes self.elephant = Elephant(self) self.shark_active = False #is the shark enabled yet self.elephant_active = False self.cat = Cat(self) self.score_text = Score(self) self.deadzones = [] # self.deadzones = [ # DeadZone( # [ # [0, height - 100], # [0.1 * width, height - 100], # [0.1 * width, height], # [0, height], # ], # ), # DeadZone( # [ # [0.9 * width, height - 100], # [width, height - 100], # [width, height], # [0.9 * width, height], # ], # ), # ] self.init_sprites() # lists of things to catch by [posx, posy, velx, vely] # self.fish = [[0, height / 2, 10, -5]] self.fish = LayeredDirtyAppend() self.fish.extend([Fish(self.allsprites, 0, height / 2, 10, -5)]) self.not_fish = LayeredDirtyAppend() #difficulty varibles self.number_of_not_fish = 0 def init_sprites(self): """temp, this will go in the init. """ sprite_list = [ self.elephant, self.cat, self.score_text ] sprite_list += self.deadzones self.allsprites = LayeredDirty( sprite_list, _time_threshold=1000/10.0 ) scene = self self.shark = Shark(self.allsprites, scene, self.width, self.height) self.allsprites.add(self.shark) self.allsprites.clear(self.screen, self.background) #what to do when you die, reset the level def reset_on_death(self): self.cat_location = [self.width / 2, self.height - 100] self.cat_speed = [0, 0] self.cat_angle = 0 self.cat_angular_vel = 0 self.score = 0 self.total_time = 0 self.elephant.last_animation = 0 self.elephant.state = 0 self.elephant.just_happened = None self.elephant.dirty = 1 self.elephant_active = False self.shark.last_animation = 0 self.shark.state = 0 self.shark_active = False self.shark.just_happened = None self.shark.dirty = 1 if hasattr(self.shark, 'lazer'): self.shark.lazer.kill() #periodically increase the difficulty def increase_difficulty(self): self.number_of_not_fish = 0 if self.score > 3: self.number_of_not_fish = 1 if self.score > 9: self.number_of_not_fish = 1 if self.score > 15: self.number_of_not_fish = 2 if self.score > 19: self.number_of_not_fish = 1 if self.score > 25: self.number_of_not_fish = 2 if self.score > 35: self.number_of_not_fish = 3 if self.score >= 50: self.number_of_not_fish = int((self.score - 20)/10) #TODO: to make it easier to test. # if self.score >= 15: # self.shark_active = True if self.score >= 10: self.shark_active = True #TODO: to make it easier to test. if self.score >= 20: self.elephant_active = True def render_sprites(self): rects = [] self.allsprites.update() rects.extend(self.allsprites.draw(self.screen)) return rects def render(self): rects = [] if self.first_render: self.first_render = False rects.append(self.screen.get_rect()) rects.extend(self.render_sprites()) return rects # we draw the sprites, and then the lines over the top. self.render_sprites() screen = self.screen width, height = self.width, self.height if 0: background_colour = (0, 0, 0) screen.fill(background_colour) screen.blit(self.background, (0, 0)) self.elephant.render(screen, width, height) self.shark.render(screen, width, height) # draw cat pygame.draw.line( screen, [0, 0, 255], self.cat_location, self.cat_head_location, 20 ) pygame.draw.circle(screen, [0, 0, 255], self.cat_head_location, 50, 1) pygame.draw.circle(screen, [0, 255, 0], self.cat_head_location, 100, 1) # draw dead zones pygame.draw.polygon( screen, [255, 0, 0], [ [0, height - 100], [0.1 * width, height - 100], [0.1 * width, height], [0, height], ], ) pygame.draw.polygon( screen, [255, 0, 0], [ [0.9 * width, height - 100], [width, height - 100], [width, height], [0.9 * width, height], ], ) # draw fish and not fish for f in self.fish: pygame.draw.circle(screen, [0, 255, 0], [int(f.pos[0]), int(f.pos[1])], 10) for f in self.not_fish: pygame.draw.circle(screen, [255, 0, 0], [int(f.pos[0]), int(f.pos[1])], 10) # draw score textsurface = self.myfont.render(str(self.score), True, [0, 0, 0] ) screen.blit(textsurface, (200, 300)) return [screen.get_rect()] def tick(self, dt): self.increase_difficulty() self.total_time += dt #keep track of the total number of ms passed during the game dt_scaled = dt/17 self.dt_scaled = dt_scaled width, height = self.width, self.height ##cat physics self.cat_angular_vel *= 0.9**dt_scaled #max(0.9/(max(0.1,dt_scaled)),0.999) # add gravity self.cat_speed[1] = min(self.cat_speed[1] + (1 * dt_scaled), self.cat_fall_speed_max) # accelerate the cat left or right if self.right_pressed: self.cat_speed[0] = min( self.cat_speed[0] + 0.3 * dt_scaled, self.cat_speed_max ) self.cat_angle -= 0.003 * dt_scaled if self.left_pressed: self.cat_speed[0] = max( self.cat_speed[0] - 0.3 * dt_scaled, -self.cat_speed_max ) self.cat_angle += 0.003 * dt_scaled # make the cat fall angle_sign = 1 if self.cat_angle > 0 else -1 self.cat_angular_vel += 0.0002 * angle_sign * dt_scaled self.cat_angle += self.cat_angular_vel * dt_scaled if (self.cat_angle > math.pi / 2 or self.cat_angle < -math.pi / 2) and self.cat_location[1] > height - 160: self.reset_on_death() # move cat self.cat_location[0] += self.cat_speed[0] * dt_scaled self.cat_location[1] += self.cat_speed[1] * dt_scaled if self.cat_location[1] > self.cat_wire_height and self.cat_location[0] > 0.25 * width: self.cat_location[1] = self.cat_wire_height self.cat_speed[1] = 0 if self.cat_location[1] > height: self.reset_on_death() if self.cat_location[0] > width: self.cat_location[0] = width if self.cat_angle > 0: self.cat_angle *= 0.7 self.cat_head_location = [ int(self.cat_location[0] + 100 * math.cos(self.cat_angle - math.pi / 2)), int(self.cat_location[1] + 100 * math.sin(self.cat_angle - math.pi / 2)), ] # check for out of bounds if self.cat_location[0] > 0.98 * width and self.cat_location[1] > self.cat_wire_height - 30: #bump the cat back in self.cat_angular_vel -= 0.01*dt_scaled self.cat_speed[0] = -5 self.cat_speed[1] = -20 #self.reset_on_death() if self.cat_location[0] < 0.25 * width and self.cat_location[1] > self.cat_wire_height - 30: pass #check for collision with the elephant stomp if self.elephant_active: self.elephant.animate(self.total_time) self.elephant.collide(self, width, height, self.cat_head_location) if self.shark_active: self.shark.animate(self.total_time) self.shark.collide(self, width, height, self.cat_location) ##object physics # move fish and not fish for f in reversed(self.fish.sprites()): f.pos[0] += f.velocity[0] * dt_scaled # speed of the throw f.velocity[1] += 0.2 * dt_scaled # gravity f.pos[1] += f.velocity[1] * dt_scaled # y velocity # check out of bounds if f.pos[1] > height: self.fish.remove(f) f.kill() for f in reversed(self.not_fish.sprites()): f.pos[0] += f.velocity[0] * dt_scaled # speed of the throw f.velocity[1] += 0.2 * dt_scaled # gravity f.pos[1] += f.velocity[1] * dt_scaled # y velocity # check out of bounds if f.pos[1] > height: self.not_fish.remove(f) f.kill() # check collision with the cat for f in reversed(self.fish.sprites()): if distance([f.rect[0], f.rect[1]], self.cat_head_location) < 100: self.score += 1 self.fish.remove(f) sfx('eatfish.ogg', play=1) f.kill() for f in reversed(self.not_fish.sprites()): if distance([f.rect[0], f.rect[1]], self.cat_head_location) < 50: self.not_fish.remove(f) f.kill() self.angle_to_not_fish = ( math.atan2( self.cat_head_location[1] - f.rect[1], self.cat_head_location[0] - f.rect[0], ) - math.pi / 2 ) side = 1 if self.angle_to_not_fish < 0 else -1 self.cat_angular_vel += side * random.uniform(0.08, 0.15) # refresh lists while len(self.fish) < 1: # choose a side of the screen if random.choice([0, 1]) == 0: self.fish.append( Fish(self.allsprites, 0, height/2,#random.randint(0, height / 2), random.randint(3, 7), -random.randint(5, 12), ) ) else: self.fish.append( Fish(self.allsprites, width, height/2,#random.randint(0, height / 2), -random.randint(3, 7), -random.randint(5, 12), ) ) while len(self.not_fish) < self.number_of_not_fish: # choose a side of the screen if random.choice([0, 1]) == 0: self.not_fish.append( NotFish(self.allsprites, 0, height/2,#random.randint(0, height / 2), random.randint(3, 7), -random.randint(5, 12), ) ) else: self.not_fish.append( NotFish(self.allsprites, width, height/2,#random.randint(0, height / 2), -random.randint(3, 7), -random.randint(5, 12), ) ) def event(self, event): width, height = self.width, self.height if event.type == KEYDOWN: if event.key == K_RIGHT: self.right_pressed = True # cat_speed[0] = min(cat_speed[0] + 2, cat_speed_max) # cat_angle -= random.uniform(0.02*math.pi, 0.05*math.pi) elif event.key == K_LEFT: self.left_pressed = True # cat_speed[0] = min(cat_speed[0] - 2, cat_speed_max) # cat_angle += random.uniform(0.02*math.pi, 0.05*math.pi) elif event.key == K_a: self.cat_angular_vel -= random.uniform(0.01 * math.pi, 0.03 * math.pi) elif event.key == K_d: self.cat_angular_vel += random.uniform(0.01 * math.pi, 0.03 * math.pi) elif event.key == K_UP: if self.cat_location[1] > self.cat_wire_height - 1: self.cat_speed[1] -= 25 sfx('cat_jump.ogg', play=1) elif event.type == KEYUP: if event.key == K_UP: if self.cat_speed[1] < 0: self.cat_speed[1] = 0 elif event.key == K_RIGHT: self.right_pressed = False elif event.key == K_LEFT: self.left_pressed = False
class Screen(object): def __init__(self, name): State.name = name State.screen = self State.controls = State.screens[State.name]['controls'] State.groups = {} self.layers = LayeredDirty() self.add_all() State.save(State.name) def add_all(self): """Add all the objects specified in the screen's configuration resource to their proper sprite groups for rendering.""" for obj in State.screens[State.name]['objects']: self.add_object(obj) def add_object(self, name, amount=1, pos=None): """Add one or many of a single game object resource to the screen. name: the name of the game object. amount: the amount of instances to add. pos: if value is 'random', every object will start in a random location. if value is a (x,y) tuple, every object will start at that screen location.""" obj = State.objects[name] new_pos = None for i in range(0, amount): if pos == 'random': scr = State.window.get_size() spr = obj['size'] new_pos = (randint(0, scr[0]/spr[0]), randint(0, scr[1]/spr[1])) elif type(pos) == type(tuple()): new_pos = pos if new_pos: obj['pos'] = new_pos group = obj['group'] if group not in State.groups: State.groups[group] = Group() sprite = eval(group.capitalize())(obj) State.groups[group].add(sprite) if obj['cursor']: State.cursor = sprite self.layers.add(State.groups[group]) def draw(self): """Run the update method of every sprite, keeping track of which ones are dirty and need updating, and then finally updating only the dirty areas.""" self.layers.update() State.dirty = self.layers.draw(State.window) display.update(State.dirty) def switch(self, name): """Switch to a new screen by saving the current state, and then restoring the specified state.""" State.save(State.name) State.prev_name = State.name State.restore(name) def restore(self): """Called when a screen is restored from a saved state.""" State.pressed = [] for group in State.groups: for sprite in State.groups[group]: sprite.dirty = 1 sprite.stopped = True
class MapView: def __init__(self, action_handler, index): # Init clock self.clock = pyg.time.Clock() # Set handler self.action_handler = action_handler # Init groupsView self.all_sprites = LayeredDirty() Fps.containers += (self.all_sprites,) # Create window self.screen, self.background = reset_screen() if DISPLAY_FPS: Fps(self.clock) # Blit level image, rect = get_stage_image(index) self.background.blit(image, rect) # Tile handling from TileView import TileView TileView.layer_container = self.all_sprites # Initialize attributes self.exit = False self.done = False self.countdown = None def win(self): self.done = True self.win = True self.countdown = countdown(GoalView.len_animation) def lose(self, nb_tiles): self.done = True self.win = False value = MinimizingPlayerView.len_animation value += TeleportingPlayerView.len_animation * (nb_tiles-2) value += FallingPlayerView.len_animation value *= 2 self.countdown = countdown(value) def reactor_loop(self): # Infinite loop while True: # Get input for ev in pyg.event.get(): # Quit if (ev.type == pyg.KEYDOWN and ev.key == pyg.K_ESCAPE)\ or ev.type == pyg.QUIT: safe_exit() # Reset if ev.type == pyg.JOYBUTTONDOWN and \ ev.button in RESET_BUTTONS: win_reset = False, True return win_reset # Handle countdown if self.done and next(self.countdown): self.all_sprites.empty() win_reset = self.win, False return win_reset # Read input if not self.done: self.action_handler.read_inputs() # Clear sprites from screen self.all_sprites.clear(self.screen, self.background) # Update sprites self.all_sprites.update() # Draw sprites on screen dirty = self.all_sprites.draw(self.screen) # Update display pyg.display.flip() # Frame rate control self.clock.tick(FPS)
class CatUniScene(Scene): # pylint:disable=too-many-instance-attributes """Cat unicycle scene.""" def __init__(self, *args, **kwargs): Scene.__init__(self, *args, **kwargs) (width, height) = (1920 // 2, 1080 // 2) self.width, self.height = width, height # Loading screen should always be a fallback active scene self.active = False self.first_render = True self.myfont = pygame.font.SysFont("monospace", 20) self.background = gfx("background.png", convert=True) # self.cat_unicycle = gfx('cat_unicycle.png').convert_alpha() # self.fish = gfx('fish.png').convert_alpha() # self.foot = gfx('foot.png').convert_alpha() # self.foot_part = gfx('foot_part.png').convert_alpha() # self.shark = gfx('shark.png').convert_alpha() sfx("cat_jump.ogg") sfx("eatfish.ogg") sfx("splash.ogg") sfx("cat_crash.ogg") self.meow_names = [ "cat_meow01.ogg", "cat_meow02.ogg", "cat_meow03.ogg" ] self.last_meow = None self.touching_ground = True self.jumping = False self.jumping_time = 0 self.jump_key = None for meow_name in self.meow_names: sfx(meow_name) self.boing_names = ["boing1.ogg", "boing2.ogg", "boing3.ogg"] for boing_name in self.boing_names: sfx(boing_name) self.people_mad = False self.people_mad_duration = 3000 # ms self.people_mad_current_time = 0 self.next_notfish = 0 self.notfish_time = 0 self.last_joy_right_tilt = 0 self.last_joy_left_tilt = 0 self.left_pressed = False self.right_pressed = False self.player_data = PlayerData(width, height) # timing self.dt_scaled = 0 self.total_time = 0 # elephant and shark classes self.elephant = Elephant(self) self.shark_active = False # is the shark enabled yet self.elephant_active = False self.cat = Cat(self) self.score_text = Score(self) self.allsprites = None # type: Optional[LayeredDirty] self.shark = None # type: Optional[Shark] self.init_sprites() # lists of things to catch by [posx, posy, velx, vely] # self.fish = [[0, height / 2, 10, -5]] self.fish = LayeredDirtyAppend() self.fish.extend([Fish(self.allsprites, (0, height / 2), (10, -5))]) self.not_fish = LayeredDirtyAppend() self.unicycle_sound = sfx("unicycle.ogg", play=True, loops=-1, fadein=500) self._reset_meow() # difficulty varibles self.number_of_not_fish = 0 def _reset_meow(self): self.next_meow = random.uniform(5000, 10000) def _meow(self): # Play a meow sound, but not the same one twice in a row meow_names = self.meow_names[:] if self.last_meow in self.meow_names: meow_names.remove(self.last_meow) self.last_meow = random.choice(meow_names) sfx(self.last_meow, play=1) self._reset_meow() def init_sprites(self): """temp, this will go in the init.""" sprite_list = [self.elephant, self.cat, self.score_text] self.allsprites = LayeredDirty(sprite_list, _time_threshold=1000 / 10.0) scene = self self.shark = Shark(self.allsprites, scene, self.width, self.height) self.allsprites.add(self.shark) self.allsprites.clear(self.screen, self.background) def reset_on_death(self): """Reset on death. What to do when you die, reset the level. """ self.player_data.reset() self.total_time = 0 self.elephant.last_animation = 0 self.elephant.state = 0 self.elephant.just_happened = None self.elephant.dirty = 1 self.elephant_active = False self.elephant.animate(self.total_time) # make the shark leave self.shark_active = False self.shark.last_animation = 0 self.shark.dirty = True if self.shark.get_state() in ("aiming", "fire laser"): self.shark.just_happened = None self.shark.set_state("leaving") self.shark.applaud = False else: self.shark.just_happened = None self.shark.set_state("offscreen") self.shark.animate(self.total_time) sfx("shark_appear.ogg", fadeout=1000) if self.shark.lazer: self.shark.lazer.kill() def increase_difficulty(self): """ Periodically increase the difficulty.""" self.number_of_not_fish = 0 if self.player_data.score > 3: self.number_of_not_fish = 1 if self.player_data.score > 9: self.number_of_not_fish = 1 if self.player_data.score > 15: self.number_of_not_fish = 2 if self.player_data.score > 19: self.number_of_not_fish = 1 if self.player_data.score > 25: self.number_of_not_fish = 2 if self.player_data.score > 35: self.number_of_not_fish = 3 if self.player_data.score >= 50: self.number_of_not_fish = int((self.player_data.score - 20) / 10) if self.player_data.score >= 10: self.shark_active = True # Elephant doesn't work yet, so let's not use it # if self.player_data.score >= 20: # self.elephant_active = True def annoy_crowd(self): """ Annoy the crowd.""" self.people_mad = True self.people_mad_current_time = 0 def render_sprites(self): """ Render the sprites.""" rects = [] self.allsprites.update(time_delta=self.dt_scaled, height=self.height, player_data=self.player_data) rects.extend(self.allsprites.draw(self.screen)) return rects def render(self): rects = [] if self.first_render: self.first_render = False rects.append(self.screen.get_rect()) rects.extend(self.render_sprites()) return rects def tick(self, time_delta): self.increase_difficulty() self.cat.animate(time_delta) self.total_time += ( time_delta # keep track of the total number of ms passed during the game ) dt_scaled = time_delta / 17 self.dt_scaled = dt_scaled width, height = self.width, self.height ##cat physics self.player_data.cat_angular_vel *= ( 0.9**dt_scaled) # max(0.9/(max(0.1,dt_scaled)),0.999) # make the cat slide in the direction it's rotated self.player_data.cat_speed[0] += math.sin( self.player_data.cat_angle) * (dt_scaled * self.player_data.cat_roll_speed) # add gravity self.player_data.cat_speed[1] = min( self.player_data.cat_speed[1] + (1 * dt_scaled), self.player_data.cat_fall_speed_max, ) self.unicycle_sound.set_volume( abs(self.player_data.cat_speed[0] / self.player_data.cat_speed_max)) self._move_cat() self._cat_out_of_bounds() # check for collision with the elephant stomp if self.elephant_active: self.elephant.animate(self.total_time) self.elephant.collide(width) if self.shark_active or self.shark.states[ self.shark.state] == "leaving": self.shark.animate(self.total_time) self.shark.collide(self, width, height, self.player_data.cat_location) self._cat_jumping(time_delta) self._cats_meow(time_delta) self._angry_people(time_delta) self._collide_flying_objects() self._spawn_flying_objects() def _move_cat(self): """Move, accelerate, and tilt the cat.""" # accelerate the cat left or right if self.right_pressed: self.player_data.cat_speed[0] = min( self.player_data.cat_speed[0] + 0.3 * self.dt_scaled, self.player_data.cat_speed_max, ) self.player_data.cat_angle -= 0.003 * self.dt_scaled if self.left_pressed: self.player_data.cat_speed[0] = max( self.player_data.cat_speed[0] - 0.3 * self.dt_scaled, -self.player_data.cat_speed_max, ) self.player_data.cat_angle += 0.003 * self.dt_scaled # make the cat fall angle_sign = 1 if self.player_data.cat_angle > 0 else -1 self.player_data.cat_angular_vel += 0.0002 * angle_sign * self.dt_scaled self.player_data.cat_angle += self.player_data.cat_angular_vel * self.dt_scaled if (self.player_data.cat_angle > math.pi / 2 or self.player_data.cat_angle < -math.pi / 2 ) and self.player_data.cat_location[1] > self.height - 160: sfx("cat_crash.ogg", play=1) self.reset_on_death() # move cat self.player_data.cat_location[0] += (self.player_data.cat_speed[0] * self.dt_scaled) self.player_data.cat_location[1] += (self.player_data.cat_speed[1] * self.dt_scaled) if (self.player_data.cat_location[1] > self.player_data.cat_wire_height and self.player_data.cat_location[0] > 0.25 * self.width): self.touching_ground = True self.player_data.cat_location[1] = self.player_data.cat_wire_height self.player_data.cat_speed[1] = 0 else: self.touching_ground = False def _cat_out_of_bounds(self): """check for out of bounds""" # in the pool if self.player_data.cat_location[1] > self.height: sfx("splash.ogg", play=1) self._meow() self.reset_on_death() # to the right of screen. if self.player_data.cat_location[0] > self.width: self.player_data.cat_location[0] = self.width if self.player_data.cat_angle > 0: self.player_data.cat_angle *= 0.7 self.player_data.cat_head_location = [ int(self.player_data.cat_location[0] + 100 * math.cos(self.player_data.cat_angle - math.pi / 2)), int(self.player_data.cat_location[1] + 100 * math.sin(self.player_data.cat_angle - math.pi / 2)), ] if (self.player_data.cat_location[0] > 0.98 * self.width and self.player_data.cat_location[1] > self.player_data.cat_wire_height - 30): # bump the cat back in self._meow() sfx(random.choice(self.boing_names), play=True) self.player_data.cat_angular_vel -= 0.01 * self.dt_scaled self.player_data.cat_speed[0] = -5 self.player_data.cat_speed[1] = -20 # self.reset_on_death() if (self.player_data.cat_location[0] < 0.25 * self.width and self.player_data.cat_location[1] > self.player_data.cat_wire_height - 30): pass def _cat_jumping(self, time_delta): """jumping physics""" if self.jumping: self.player_data.cat_speed[1] -= ( time_delta * ((CAT_MAX_JUMPING_TIME - self.jumping_time) / CAT_MAX_JUMPING_TIME) * CAT_JUMP_SPEED) self.jumping_time += time_delta if self.jumping_time >= CAT_MAX_JUMPING_TIME: self.jumping = False def _cats_meow(self, time_delta): """meow timing""" if self.next_meow <= 0: self._meow() self.next_meow -= time_delta def _angry_people(self, time_delta): """angry people (increased throwing of not-fish)""" if self.people_mad: self.people_mad_current_time += time_delta self.notfish_time += time_delta if self.notfish_time >= self.next_notfish: self.next_notfish = random.randint(100, 400) self.notfish_time = 0 self._spawn_not_fish() if self.people_mad_current_time >= self.people_mad_duration: self.people_mad = False def _collide_flying_objects(self): """object physics""" height = self.height dt_scaled = self.dt_scaled # move fish and not fish for fish in reversed(self.fish.sprites()): fish.pos[0] += fish.velocity[0] * dt_scaled # speed of the throw fish.velocity[1] += 0.2 * dt_scaled # gravity fish.pos[1] += fish.velocity[1] * dt_scaled # y velocity # check out of bounds if fish.pos[1] > height: self.fish.remove(fish) fish.kill() for fish in reversed(self.not_fish.sprites()): fish.pos[0] += fish.velocity[0] * dt_scaled # speed of the throw fish.velocity[1] += 0.2 * dt_scaled # gravity fish.pos[1] += fish.velocity[1] * dt_scaled # y velocity # check out of bounds if fish.pos[1] > height: self.not_fish.remove(fish) fish.kill() # check collision with the cat for fish in reversed(self.fish.sprites()): if (distance([fish.rect[0], fish.rect[1]], self.player_data.cat_head_location) < 100): self.player_data.increment_score() self.fish.remove(fish) sfx("eatfish.ogg", play=1) fish.kill() for fish in reversed(self.not_fish.sprites()): if (distance([fish.rect[0], fish.rect[1]], self.player_data.cat_head_location) < 50): self.not_fish.remove(fish) fish.kill() self.player_data.angle_to_not_fish = (math.atan2( self.player_data.cat_head_location[1] - fish.rect[1], self.player_data.cat_head_location[0] - fish.rect[0], ) - math.pi / 2) side = 1 if self.player_data.angle_to_not_fish < 0 else -1 self.player_data.cat_angular_vel += side * random.uniform( 0.08, 0.15) sfx(random.choice(self.boing_names), play=True) def _spawn_flying_objects(self): """Throws random objects at the cat.""" width, height = self.width, self.height # refresh lists while len(self.fish) < 1 and not self.people_mad: # choose a side of the screen if random.choice([0, 1]) == 0: self.fish.append( Fish( self.allsprites, (0, height / 2), # random.randint(0, height / 2), (random.randint(3, 7), -random.randint(5, 12)), )) else: self.fish.append( Fish( self.allsprites, (width, height / 2), # random.randint(0, height / 2), (-random.randint(3, 7), -random.randint(5, 12)), )) while len(self.not_fish) < self.number_of_not_fish: self._spawn_not_fish() def _spawn_not_fish(self): """Choose a side of the screen.""" velocity_multiplier = 1 x_pos = 0 if random.randint(0, 1): velocity_multiplier *= -1 x_pos = self.width self.not_fish.append( NotFish( self.allsprites, (x_pos, self.height / 2), (random.randint(3, 7) * velocity_multiplier, -random.randint(5, 12)), )) def _start_jump(self, key): self.jump_key = key if self.touching_ground and not self.jumping: self.jumping = True self.jumping_time = 0 self.player_data.cat_speed[1] -= 12.5 sfx("cat_jump.ogg", play=1) def _stop_jump(self): self.jumping = False sfx("cat_jump.ogg", fadeout=50) def _tilt_left(self): self.player_data.cat_angular_vel -= random.uniform( 0.01 * math.pi, 0.03 * math.pi) def _tilt_right(self): self.player_data.cat_angular_vel += random.uniform( 0.01 * math.pi, 0.03 * math.pi) def _event_keydown(self, event): if event.key == pygame.K_RIGHT: self.right_pressed = True elif event.key == pygame.K_LEFT: self.left_pressed = True elif event.key == pygame.K_a: self._tilt_left() elif event.key == pygame.K_d: self._tilt_right() elif event.key in (pygame.K_UP, pygame.K_SPACE): self._start_jump(event.key) def _event_keyup(self, event): if event.key == self.jump_key: self._stop_jump() elif event.key == pygame.K_RIGHT: self.right_pressed = False elif event.key == pygame.K_LEFT: self.left_pressed = False def _event_joybuttondown(self, event): if event.button in JOY_JUMP_BUTTONS: self._start_jump("JOY" + str(event.button)) if event.button in JOY_LEFT_BUTTONS: self._tilt_left() if event.button in JOY_RIGHT_BUTTONS: self._tilt_right() def _event_joybuttonup(self, event): if "JOY" + str(event.button) == self.jump_key: self._stop_jump() def _event_joyaxismotion(self, event): if event.axis == 0: if event.value >= JOY_SENSE: self.right_pressed = True self.left_pressed = False elif event.value <= -JOY_SENSE: self.right_pressed = False self.left_pressed = True else: self.right_pressed = False self.left_pressed = False if event.axis == JOY_TILT_RIGHT_AXIS: # if self.last_joy_right_tilt < JOY_SENSE and event.value >= JOY_SENSE: if self.last_joy_right_tilt < JOY_SENSE < event.value: self._tilt_right() self.last_joy_right_tilt = event.value if event.axis == JOY_TILT_LEFT_AXIS: # if self.last_joy_left_tilt < JOY_SENSE and event.value >= JOY_SENSE: if self.last_joy_left_tilt < JOY_SENSE < event.value: self._tilt_left() self.last_joy_left_tilt = event.value def event(self, event): if event.type == pygame.KEYDOWN: self._event_keydown(event) elif event.type == pygame.KEYUP: self._event_keyup(event) elif event.type == pygame.JOYBUTTONDOWN: self._event_joybuttondown(event) elif event.type == pygame.JOYBUTTONUP: self._event_joybuttonup(event) elif event.type == pygame.JOYAXISMOTION: self._event_joyaxismotion(event)
class MapView: def __init__(self, action_handler, index): # Init clock self.clock = pyg.time.Clock() # Set handler self.action_handler = action_handler # Init groupsView self.all_sprites = LayeredDirty(_use_updates = True, _time_threshold = 1000) Fps.containers += (self.all_sprites,) # Create window self.screen, self.background = reset_screen() if DISPLAY_FPS: Fps(self.clock) # Blit level image, rect = get_stage_image(index) self.background.blit(image, rect) self.screen.blit(self.background, self.background.get_rect()) # Tile handling from TileView import TileView TileView.layer_container = self.all_sprites # Initialize attributes self.exit = False self.done = False self.countdown = None def win(self): self.done = True self.win = True self.countdown = countdown(GoalView.len_animation) def lose(self, nb_tiles): self.done = True self.win = False value = MinimizingPlayerView.len_animation value += TeleportingPlayerView.len_animation * (nb_tiles-2) value += FallingPlayerView.len_animation value *= 2 self.countdown = countdown(value) def reactor_loop(self): # Infinite loop while True: # Get input for ev in pyg.event.get(): # Quit if (ev.type == pyg.KEYDOWN and ev.key == pyg.K_ESCAPE)\ or ev.type == pyg.QUIT: safe_exit() # Fullscreen if ev.type == pyg.KEYDOWN and ev.key == pyg.K_f: # Toggle fullscreen Constants.FULLSCREEN ^= True # Reset screen self.screen, _ = reset_screen() args = self.background, self.background.get_rect() self.screen.blit(*args) # Repaint all self.all_sprites._use_update = False # Mute if ev.type == pyg.KEYDOWN and ev.key == pyg.K_SEMICOLON: volume = 0 if pyg.mixer.music.get_volume() else VOLUME pyg.mixer.music.set_volume(float(volume)/100) # Reset if (ev.type == pyg.KEYDOWN and ev.key == pyg.K_r) or\ (ev.type == pyg.JOYBUTTONDOWN and ev.button in RESET_BUTTONS): win_reset = False, True return win_reset # Handle countdown if self.done and next(self.countdown): self.all_sprites.empty() win_reset = self.win, False return win_reset # Read input if not self.done: self.action_handler.read_inputs() # Clear sprites from screen self.all_sprites.clear(self.screen, self.background) # Update sprites self.all_sprites.update() # Draw sprites on screen dirty = self.all_sprites.draw(self.screen) # Update display pyg.display.update(dirty) # Frame rate control self.clock.tick(FPS)
class Screen(object): def __init__(self, name): State.name = name State.screen = self State.controls = State.screens[State.name]['controls'] State.groups = util.GroupCache() self.layers = LayeredDirty() self.add_all() State.save(State.name) def new_game(self): self.switch('world') def quit(self): if State.name == 'title': State.running = False else: self.switch(State.prev_name) def menu_up(self): pass def menu_down(self): pass def add_all(self): """Add all the objects specified in the screen's configuration resource to their proper sprite groups for rendering.""" for obj in State.screens[State.name]['objects']: self.add_object(obj) def add_object(self, name, group=None, pos=None): """Add a game object resource to the screen. name: the name of the game object. pos: object will be placed on this tile.""" # Get object data from given name data = State.objects[name] # If group is not set, use object's default group. if not group: group = data['group'] # Creates a game object from the name of its group, and adds it to # the group. sprite = eval(group.capitalize())(data) State.groups[group].add(sprite) # Set the object's starting tile. if pos: data['pos'] = sprite.set_pos(data, pos) # If an object is a player, tell the state so it can be controlled. # TODO: Make State.player a list of sprites. if sprite in State.groups['player']: State.player = sprite # Add the sprite groups to the rendering queue. self.layers.add(State.groups[group]) return sprite def draw(self): """Run the update method of every sprite, keeping track of which ones are dirty and need updating, and then finally updating only the dirty areas.""" self.layers.update() State.dirty = self.layers.draw(State.window) display.update(State.dirty) def switch(self, name): """Switch to a new screen by saving the current state, and then restoring the specified state.""" State.save(State.name) State.prev_name = State.name State.restore(name) def restore(self): """Called when a screen is restored from a saved state.""" State.pressed = [] for group in State.groups: for sprite in State.groups[group]: sprite.dirty = 1 sprite.stopped = True