def tick(self, events):
        self.add_new_threads()
        if self.drag_sprite:
            (mx, my) = self.screen.get_mouse_pos()
            (ox, oy) = self.drag_offset
            new_position = (mx + ox, my + oy)
            if self.drag_sprite.position != new_position:
                self.has_dragged = True
                self.drag_sprite.position = new_position
        for event in events:
            if event.kind == "key_pressed":
                assert event.value in kurt.Insert(None, "key").options()
                self.trigger_hats("whenKeyPressed", event.value)
            elif event.kind == "mouse_down":
                mouse_pos = self.screen.get_mouse_pos()
                for sprite in reversed(self.project.sprites):
                    rect = bounds(sprite)
                    if rect.collide_point(mouse_pos):
                        if self.screen.touching_mouse(sprite):
                            scriptable = sprite
                            break
                else:
                    scriptable = self.project.stage
                if scriptable.is_draggable:
                    (mx, my) = self.screen.get_mouse_pos()
                    (x, y) = scriptable.position
                    self.drag_offset = (x - mx, y - my)
                    self.drag_sprite = scriptable
                    self.has_dragged = False
                    go_to_front(scriptable)
                else:
                    self.trigger_scriptable_hats(scriptable, "whenClicked")
            elif event.kind == "mouse_up":
                if self.drag_sprite:
                    if not self.has_dragged:
                        self.trigger_scriptable_hats(self.drag_sprite,
                                                     "whenClicked")
                    self.drag_sprite = None
        remove_threads = []
        while 1:
            for (script, thread) in self.threads.items():
                modified = False
                for event in thread.tick():
                    if event.kind == "stop":
                        if event.value == "all":
                            self.stop()
                            return
                        elif event.value == "other scripts in sprite":
                            for (script, other) in self.threads.items():
                                if other.scriptable == thread.scriptable:
                                    other.finish()
                                    del self.threads[script]
                            modified = True
                            break
                        else:
                            thread.finish()
                            del self.threads[script]
                            modified = True
                            break
                    else:
                        yield event
                if modified:
                    break
            else:
                break
        self.add_new_threads()