class Stage(pyglet.event.EventDispatcher): progress_bar = pyglet.sprite.Sprite(pyglet.resource.image('img/gui/progress_bar.png'), 0, 344) progress_ball = pyglet.sprite.Sprite(pyglet.resource.image('img/gui/progress_ball.png'), 0, 344) def __init__(self, name): self.name = name self.sections = [] self.game_objects = set() self.offset = VECTOR_NULL self.is_scrolling = False self.actual_scroll_speed = VECTOR_NULL self.target_scroll_speed = SCROLL_SPEED self.active_section = None self.despawned_objects = set() log.debug(D_INIT.format(type(self).__name__, self.name)) def setup(self): self.reset() if self.seed is not None: random.seed(self.seed) bg_pattern = pyglet.image.SolidColorImagePattern( self.background_color) bg_img = bg_pattern.create_image(640, 360) # TODO: Magic number. self.background = SH.show_sprite(SH.BG, 0) self.background.image = bg_img for sect in self.sections: sect.setup() self.section_iter = iter(self.sections) self.advance_section() self.is_scrolling = True self.hero = Hero(Vector(100, 100)) self.spawn_game_objects(set([self.hero])) self.hero.push_handlers(self) def reset(self): self.despawn_game_objects(self.game_objects) self.actual_scroll_speed = VECTOR_NULL self.offset = VECTOR_NULL self.active_section = None log.info('Reset Stage {}.'.format(self.name)) def update(self, dt): self.update_stage_position(dt) self.update_game_objects(dt) self.update_sprites() self.check_collisions() self.update_progress_ball() def update_stage_position(self, dt): if self.is_scrolling: self.update_scroll_speed(dt) self.offset += self.actual_scroll_speed * dt if self.active_section is not None: if self.offset.x >= self.active_section.offset.x: self.advance_section() def update_scroll_speed(self, dt): if self.target_scroll_speed != self.actual_scroll_speed: delta = 50 * dt diff = self.target_scroll_speed - self.actual_scroll_speed self.actual_scroll_speed += diff.unit * min(delta, diff.length) def start_scrolling(self, scroll_speed): self.target_scroll_speed = scroll_speed self.is_scrolling = True def stop_scrolling(self): self.actual_scroll_speed = VECTOR_NULL self.is_scrolling = False def update_game_objects(self, dt): self.game_objects -= self.despawned_objects for obj in self.despawned_objects: obj.despawn() self.despawned_objects = set() for gob in self.game_objects: gob.update(dt) def update_sprites(self): for gob in self.game_objects: gob.align_sprite(self.offset) def advance_section(self): if self.active_section is not None: self.exit_section(self.active_section) try: new_section = self.section_iter.next() self.enter_section(new_section) except StopIteration: self.stop_scrolling() def exit_section(self, old_section): if old_section is not None: self.spawn_game_objects(old_section.second_actors) old_section.reset() self.active_section = None self.dispatch_event('on_exit_section', old_section.name) log.info(I_EXIT_SECTION.format(old_section.name)) def enter_section(self, new_section): if new_section is not None: self.spawn_game_objects(new_section.props) self.spawn_game_objects(new_section.actors) self.active_section = new_section self.dispatch_event('on_enter_section', new_section.name) log.info(I_ENTER_SECTION.format(new_section.name)) def add_section(self, section): section.offset = Vector(self.width, 0) self.sections.append(section) log.debug(D_ADD_SECTION.format(section.name, self.name)) def spawn_game_objects(self, game_objects): for gob in game_objects: gob.allocate_sprite() gob.show() gob.push_handlers(self) log.debug(D_SPAWN.format(type(gob).__name__, len(self.game_objects))) self.game_objects |= game_objects def spawn_projectile(self, projectile): projectile.allocate_sprite() projectile.align_sprite(self.offset) projectile.show() log.debug(D_SPAWN_BULLET.format(type(projectile).__name__)) self.game_objects.add(projectile) projectile.push_handlers(self) def despawn_game_objects(self, game_objects): for gob in game_objects: gob.despawn() self.game_objects -= game_objects def check_collisions(self): collisions = collider.aabb_collide(list(self.game_objects)) for group in collisions: a, b = group a.collide(b, VECTOR_NULL, VECTOR_NULL) b.collide(a, VECTOR_NULL, VECTOR_NULL) @property def current_props(self): if self.active_section is not None: return self.active_section.props else: return set() @property def width(self): return world.constants['WIN_WIDTH'] * len(self.sections) @property def height(self): return world.constants['WIN_HEIGHT'] @property def active_rect(self): return (self.offset.x - 100, 0, self.offset.x + world.constants['WIN_WIDTH'] + 100, world.constants['WIN_HEIGHT']) @property def left(self): return self.offset.x @property def right(self): return self.offset.x + world.constants['WIN_WIDTH'] @property def bottom(self): return self.offset.y @property def top(self): return self.offset.y + world.constants['WIN_HEIGHT'] def send_keys_to_hero(self, keys, pressed=None, released=None): if self.hero is not None: self.hero.fixSpeed(keys) if pressed is not None: self.hero.throw_pebble() def on_hero_moved(self, the_hero, position): if position.x < self.offset.x - 100: pass ## log.info('Hero left to the left.') elif position.x > self.offset.x + world.constants['WIN_WIDTH'] + 10: ## log.info('Hero left to the right.') if self.right < self.width: if the_hero.status != 'knockback': the_hero.send_effect('knockback') self.target_scroll_speed *= 1.2 the_hero.max_speed *= 1.2 else: self.dispatch_event('on_end_stage') def on_emit(self, projectile): self.spawn_projectile(projectile) def on_object_moved(self, obj, position): if obj.despawn_on_exit: if obj.right <= self.left: ## or obj.left >= self.right self.despawn(obj) elif obj.bottom >= self.top or obj.top <= self.bottom: self.despawn(obj) def despawn(self, obj): self.despawned_objects.add(obj) log.debug('Despawning {}'.format(type(obj).__name__)) def update_progress_ball(self): self.progress_ball.x = min(624, int(624 * self.hero.x / self.width))