예제 #1
0
    def load_room(self, num_of_room):  # загрузка комнаты на экран
        self.base = []
        level = self.rooms[num_of_room].structure()
        empty = Image.open('Sprites/ground/idle/00.png')
        wall = Image.open('Sprites/wall/idle/00.png')
        background = Image.new('RGB',
                               (len(level[0]) * TILE, len(level) * TILE),
                               (255, 255, 255))
        # собираем из маленьких изображений пустых клетов и стен
        # одно большое изображение поля чтобы потом отображать только его
        for i in range(len(level)):
            for k in range(len(level[i])):
                if level[i][k] == 'W':
                    self.base.append(Wall((k, i)))
                    background.paste(wall, (k * TILE, i * TILE))
                else:
                    self.base.append(Empty((k, i)))
                    background.paste(empty, (k * TILE, i * TILE))
        self.background = pygame.image.fromstring(background.tobytes(),
                                                  background.size,
                                                  background.mode)

        self.top_panel = Panel(self.player, 0)  # создаем верхнюю
        self.bottom_panel = Panel(None, 550)  # и нижнюю панели
        self.buttons = [  # создаем кнопки
            Button('game/panel/exit', (550, 10), 'menu'),
            Button('game/panel/inventory', (450, 10), 'inventory'),
            Button('game/panel/save', (500, 10), 'save'),
        ]
예제 #2
0
 def init_panel(self):
     self.p = Panel((AppSettings.anim_size[2], AppSettings.anim_size[3]))
     self.p.draw = self.draw
     self.p.setup_ui = self.setup_ui
     self.p.keyPressEvent = self.keyPressEvent
     self.p.init()
     return self.p
예제 #3
0
    def init_panel(self):
        self.p = Panel(AppSettings.scene_size)
        self.p.draw = self.draw
        self.p.setup_ui = self.setup_ui
        self.p.init()

        self.p.mousePressEvent = self.mousePressEvent
        self.p.keyPressEvent = self.keyPressEvent
        return self.p
예제 #4
0
    def init_panel(self):
        self.p = Panel(AppSettings.timeline_size)
        self.p.draw = self.draw
        self.p.setup_ui = self.setup_ui
        self.p.init()

        # turns off default IO
        self.p.mouseReleaseEvent = self.empty
        self.p.mouseMoveEvent = self.empty
        self.p.wheelEvent = self.empty
        self.p.keyPressEvent = self.keyPressEvent
        return self.p
예제 #5
0
class Animation(AppElementSpawnable):

    # ------------------------------------------------------------------------ #
    # Functions that will update all instances (useful for update ticks)

    @staticmethod
    def evaluate_frame(frame):
        for inst in Animation._instances:
            inst.p.update()

    @staticmethod
    def mocap_reset():
        for inst in Animation._instances:
            inst.update_motion_menu()

    def update_motion_menu(self):
        m = [(name, self.set_motion_curve_fn(name), False, False) for name in MocapHandle().get_names()]
        self.motion_menu.setMenu(Widgets.menu(self.p, m))

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # --

    def __init__(self, ref_toon, ref_name):

        # Motion
        self.ref_toon = ref_toon
        self.ref_name = ref_name
        self.mc_handle = ""

        # Algorithms
        self.params = Graph.params

        self.preprocess_curve = []
        self.keyframes = []
        self.splines = []

        # Metrics
        self.metric_map = MetricMap()

        # Display, and other
        self.image_save_index = 0
        self.display = Graph.draw_type[Graph.draw_type_default]

    def setup_ui(self):
        x = 10
        inc = 100

        self.joint_display_name = Widgets.label(self.p, self.ref_name, (x, 4))
        self.mc_handle_label = Widgets.label(self.p, self.mc_handle, (x, 24))
        x += 50

        # -------------------------------------------------------------------- #
        # Menus

        m = [("None", self.empty, False, False)]
        self.motion_menu = Widgets.button_menu(self.p, "Motion", m, (x, 0))
        self.update_motion_menu()
        x += inc

        # Application of curve
        m = [
            ("Clear", self.clear_motion, False, False),
            ("tx", self.apply_curve_fn("tx"), False, False),
            ("ty", self.apply_curve_fn("ty"), False, False),
            ("rz", self.apply_curve_fn("rz"), False, False),
        ]
        Widgets.button_menu(self.p, "Apply", m, (x, 0))
        x += inc

        # Grepping curvers from existing toon
        m = [
            ("tx", self.grep_curve_fn("tx"), False, False),
            ("ty", self.grep_curve_fn("ty"), False, False),
            ("rz", self.grep_curve_fn("rz"), False, False),
        ]
        Widgets.button_menu(self.p, "Grep", m, (x, 0))
        x += inc

        # Display menu
        m = [(key, self.toggle_display_fn(key), True, self.display[key]) for key in self.display.keys()]
        Widgets.button_menu(self.p, "Display", m, (x, 0))
        x += inc

        # Algorithms menu
        m = [
            ("Subdivide", self.subdivide, False, False),
            ("Douglas Peucker", self.douglas_peucker, False, False),
            ("Salient Points 1D", self.salient_points_1d, False, False),
            ("Salient Points FT", self.salient_points_ft, False, False),
            ("Salient Points it", self.salient_points_iter, False, False),
            ("Polyline", self.polyline, False, False),
            ("Flat tangents", self.flat_tangents, False, False),
            ("One-pass", self.cubic, False, False),
            ("Reset", self.reset_simplification, False, False),
            ("Save Graph", self.save_graph, False, False),
        ]
        Widgets.button_menu(self.p, "Simplify", m, (x, 0))
        x += inc

        #
        # -------------------------------------------------------------------- #

        # -------------------------------------------------------------------- #
        # Sliders

        x = 100
        y = AppSettings.anim_size[3] - 150
        inc = 20

        self.sliders = {}
        for key in self.params.keys():
            if key != "":
                slider = Widgets.slider(
                    self.p, key, self.update_slider_fn(key), mm=(1, 20), xy=(x, y), v=self.params[key]
                )
                self.sliders[key] = slider
            y += inc

        #
        # -------------------------------------------------------------------- #

        # -------------------------------------------------------------------- #
        # Metric map

        x = AppSettings.anim_size[2] - 100
        y = AppSettings.anim_size[3] * 0.5
        m = self.metric_map.m
        self.metric_map_handle = {}

        for key in m.keys():
            inner_m = m[key]
            l = Widgets.label(self.p, key, xy=(x - 50, y))
            y += 20
            self.metric_map_handle[key] = {}

            for inner_key in inner_m:
                l = Widgets.label(self.p, inner_key, xy=(x, y))
                y += 20
                self.metric_map_handle[key][inner_key] = l

        #
        # -------------------------------------------------------------------- #

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Key map

    def keyPressEvent(self, e):

        # Switch focus to current selection
        if self.p.query_key("e", e.key()):
            index, joint = Scene().get_selected()
            self.ref_toon = index
            self.ref_name = joint.name
            self.joint_display_name.setText(self.ref_name)

        # Fast save graph
        if self.p.query_key("s", e.key()):
            self.image_save_index += 1
            index = self.image_save_index
            self.save_graph("%s/%s" % (AppSettings.default_graph_save_dir, "fast_save_" + str(index)) + ".svg")

        # Reset fast index
        if self.p.query_key("q", e.key()):
            self.image_save_index = 0

        # Auto displays
        display_presets = ["1", "2", "3", "4", "5", "6"]
        for key in display_presets:
            if self.p.query_key(key, e.key()):
                self.toggle_display_type(key)

        # Algorithm hotkeys
        if self.p.query_key("y", e.key()):
            self.subdivide()
        if self.p.query_key("u", e.key()):
            self.douglas_peucker()
        if self.p.query_key("i", e.key()):
            self.salient_points_1d()
        if self.p.query_key("o", e.key()):
            self.salient_points_ft()
        if self.p.query_key("p", e.key()):
            self.salient_points_iter()
        if self.p.query_key("j", e.key()):
            self.cubic()
        if self.p.query_key("k", e.key()):
            self.flat_tangents()
        if self.p.query_key("l", e.key()):
            self.polyline()

        # Camera controls
        if self.p.query_key("x", e.key()):
            self.p.toggle_camera_lock()
        if self.p.query_key("c", e.key()):
            self.p.store_camera()
        if self.p.query_key("v", e.key()):
            self.p.recover_camera()

        # Quit hotkey
        if self.p.query_key("esc", e.key()):
            self.p.window_obj.close()

        self.p.update()

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Panel stuff

    def init_panel(self):
        self.p = Panel((AppSettings.anim_size[2], AppSettings.anim_size[3]))
        self.p.draw = self.draw
        self.p.setup_ui = self.setup_ui
        self.p.keyPressEvent = self.keyPressEvent
        self.p.init()
        return self.p

    def draw(self, d):
        sd = -AppSettings.anim_size[2] * 0.4
        ed = +AppSettings.anim_size[2] * 0.4
        sh = -AppSettings.anim_size[3] * 0.4
        eh = +AppSettings.anim_size[3] * 0.4
        s = Timeline().start_frame
        f = Timeline().end_frame

        def lerp_over_drawing(p_i):
            x, y = p_i
            t = (x - s) / float(f - s)
            return sd + t * (ed - sd)

        def y_scaling(p_i):
            x, y = p_i
            return y * self.params["y"]

        def curve_as_points(color, curve, offset=0.0, sizable=False):
            s = 1.0
            if sizable:
                s = self.params["mocap"]

            d.fill(color, a=125)
            d.stroke((0, 0, 0), a=0)
            for i in range(len(curve)):
                x = lerp_over_drawing(curve[i])
                y = y_scaling(curve[i]) + offset
                d.circle((x, y), s)

        def curve_as_polyline(color, curve):
            d.fill((0, 0, 0), a=0)
            d.stroke(color, a=125)
            for i in range(len(curve)):
                x1 = lerp_over_drawing(curve[i - 1])
                x2 = lerp_over_drawing(curve[i])
                y1 = y_scaling(curve[i - 2])
                y2 = y_scaling(curve[i - 1])
                d.line((x1, y1), (x2, y2))

        def curve_as_steps(color, curve):
            d.fill((0, 0, 0), a=0)
            d.stroke(color, a=125)
            for i in range(len(curve)):
                x1 = lerp_over_drawing(curve[i - 1])
                x2 = lerp_over_drawing(curve[i])
                y1 = y_scaling(curve[i - 2])
                y2 = y_scaling(curve[i - 1])
                d.line((x1, y1), (x2, y1))
                d.line((x2, y1), (x2, y2))

        def curve_as_tangents(color, curve, tvs):
            d.fill((0, 0, 0), a=0)
            d.stroke(color, a=125)
            for i in range(len(curve)):
                tv = curve[i] + tvs[i]

                x1 = lerp_over_drawing(curve[i])
                y1 = y_scaling(curve[i])
                x2 = lerp_over_drawing(tv)
                y2 = y_scaling(tv)

                dv = np.array([x2 - x1, y2 - y1])
                dv = dv / np.linalg.norm(dv) * self.params["vector_size"]
                x2 = x1 + dv[0]
                y2 = y1 + dv[1]
                d.line((x1, y1), (x2, y2))

        def curve_as_normals(color, curve, tvs):
            d.fill((0, 0, 0), a=0)
            d.stroke(color, a=125)
            for i in range(len(curve)):
                tv = curve[i] + tvs[i]

                x1 = lerp_over_drawing(curve[i])
                y1 = y_scaling(curve[i])
                x2 = lerp_over_drawing(tv)
                y2 = y_scaling(tv)

                dv = np.array([x2 - x1, y2 - y1])
                dv = dv / np.linalg.norm(dv) * self.params["vector_size"]
                x2 = x1 - dv[1]
                y2 = y1 + dv[0]
                d.line((x1, y1), (x2, y2))

        def keyframes_as_dots(color, curve, keyframes):
            d.fill((0, 0, 0), a=0)
            d.stroke(color, a=255)

            d.fill(color, a=255)
            d.stroke((0, 0, 0), a=0)
            for k in keyframes:
                x = lerp_over_drawing(curve[k])
                y = y_scaling(curve[k])
                d.circle((x, y), 3)

        def spline_as_curves(color, interps):
            d.fill(color, a=255)
            d.stroke((0, 0, 0), a=0)
            for interp in interps:
                x = lerp_over_drawing(interp)
                y = y_scaling(interp)
                d.circle((x, y), 2)

        def spline_as_cubic(color, controls):
            d.fill((0, 0, 0), a=0)
            d.stroke(color, a=255)
            controls_scaled = [[lerp_over_drawing(c), y_scaling(c)] for c in controls]
            d.bezier(controls_scaled)

        def spline_as_handles(color, controls):
            controls_scaled = [[lerp_over_drawing(c), y_scaling(c)] for c in controls]
            p0, p1, p2, p3 = controls_scaled
            d.fill(color, a=255)
            d.stroke(color, a=255)
            d.line(p0, p1)
            d.line(p2, p3)
            d.stroke((0, 0, 0), a=0)
            d.box(p1, (5, 5))
            d.box(p2, (5, 5))

        if self.display["axis"]:
            d.fill((0, 0, 0), a=0)
            d.stroke(Colors.null, a=125)
            x = AppSettings.anim_size[2] * 0.5
            d.line((-x, 0), (+x, 0))
            x = lerp_over_drawing(s)
            d.line((x, sh), (x, eh))
            x = lerp_over_drawing(f)
            d.line((x, sh), (x, eh))

        if self.display["time"]:
            d.fill((0, 0, 0), a=0)
            d.stroke(Colors.time_marker, a=125)
            x = lerp_over_drawing(Timeline().current_frame)
            d.line((x, sh), (x, eh))

        # Motion curves
        if self.mc_handle != "":
            curve = MocapHandle().get_curve(self.mc_handle)

            if self.display["mocap_dots"]:
                curve_as_points(Colors.mocap, curve, sizable=True)

            if self.display["mocap_lines"]:
                curve_as_polyline(Colors.mocap, curve)

            if self.display["mocap_steps"]:
                curve_as_steps(Colors.mocap, curve)

            if self.display["preprocess"] and self.preprocess_curve != []:
                curve_as_points(Colors.preprocess, self.preprocess_curve)

            if self.display["tangents"] and self.preprocess_curve != []:
                tvs = TangentApprox.from_motion_curve(self.preprocess_curve)
                curve_as_tangents(Colors.tangents, self.preprocess_curve, tvs)

            if self.display["normals"] and self.preprocess_curve != []:
                tvs = TangentApprox.from_motion_curve(self.preprocess_curve)
                curve_as_normals(Colors.normals, self.preprocess_curve, tvs)

            if self.display["keyframes"]:
                keyframes_as_dots(Colors.keyframes, self.preprocess_curve, self.keyframes)

            if self.display["splines"]:
                for spline in self.splines:
                    spline_as_cubic(Colors.splines, spline.list_controls())

            if self.display["handles"]:
                for spline in self.splines:
                    spline_as_handles(Colors.handles, spline.list_controls())

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Menu callbacks

    def save_meta_data(self, filepath):
        curve = MocapHandle().get_curve(self.mc_handle)
        keyframes = self.keyframes
        spline_handles = [s.list_controls()[1:3] for s in self.splines]

        meta_data = {
            "motion": {
                "file": MocapHandle().last_loaded,
                "name": self.ref_name,
                "curve_key": self.mc_handle,
                "curve": curve.tolist(),
            },
            "simplify": {"keyframes": keyframes, "splines": spline_handles},
            "metric": self.metric_map.m,
        }

        f = open(filepath, "w")
        f.write(json.dumps(meta_data, sort_keys=True, indent=4, separators=(",", ": ")))
        f.close()

    def save_graph(self, filepath=""):
        if filepath == "":
            filepath = Dialogs.save_file(self.p, AppSettings.demuddle_dir)[0]

        data_fp = filepath + ".meta.json"
        self.p.paintImage(filepath)
        self.save_meta_data(data_fp)

    def set_motion_curve_fn(self, name):
        def fn():
            self.mc_handle = name
            self.mc_handle_label.setText(self.mc_handle)

        return fn

    def clear_motion(self):
        # TODO implement this
        # Scene().command_delete_motion(self.ref_toon, self.ref_name)
        self.p.update()

    def apply_curve_fn(self, target):
        def fn():
            Scene().command_add_motion(self.ref_toon, self.ref_name, target, self.mc_handle)
            self.p.update()

        return fn

    def grep_curve_fn(self, target):
        def fn():
            mobj = Scene().command_get_motion(self.ref_toon, self.ref_name, target)
            if mobj != None:
                self.mc_handle = "%s, %s" % (mobj.ref_name, mobj.ref_dof)
                self.mc_handle_label.setText(self.mc_handle)
            self.p.update()

        return fn

    def update_slider_fn(self, key):
        def fn(v):
            self.params[key] = v

        return fn

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Simplification

    def subdivide(self):
        curve = MocapHandle().get_curve(self.mc_handle)

        preprocess = Subdivide()
        preprocess.update_curve(curve)
        preprocess.update(self.params)
        self.preprocess_iters = preprocess.get_iters()
        self.preprocess_curve = preprocess.get_result()

        Scene().set_status("Curve preproccesed (Subdivision)", "normal")
        self.p.update()

    def douglas_peucker(self):
        curve = self.preprocess_curve

        keyframer = DouglasPeucker()
        keyframer.update_curve(curve)
        keyframer.update(self.params)
        self.keyframes = keyframer.get_keyframes()

        Scene().set_status("Keyframe selected (Douglas Peucker)", "normal")
        self.p.update()

    def salient_points_1d(self):
        curve = self.preprocess_curve

        keyframer = SalientPoints1D()
        keyframer.update_curve(curve)
        keyframer.learn()
        self.keyframer = keyframer

        Scene().set_status("zerotable (Salient Points, 1D)", "normal")
        self.p.update()

    def salient_points_ft(self):
        curve = self.preprocess_curve

        keyframer = SalientPointsFlatTangents()
        keyframer.update_curve(curve)
        keyframer.learn()
        self.keyframer = keyframer

        Scene().set_status("zerotable (Salient Points, FT)", "normal")
        self.p.update()

    def salient_points_iter(self):
        self.keyframer.update(self.params)
        self.keyframes = self.keyframer.get_keyframes()
        Scene().set_status("Keyframe selected (Salient Points, iter)", "normal")
        self.p.update()

    def polyline(self):
        curve = self.preprocess_curve

        keyframes = self.keyframes
        segments = zip(keyframes[:-1], keyframes[1:])

        self.splines = []
        for (k1, k2) in segments:
            spline_obj = PolyLine()
            spline_obj.update_curve(curve[k1 : k2 + 1])
            spline_obj.update(self.params)
            spline_obj.run()
            self.splines.append(spline_obj)

        self.update_metric_map()
        Scene().set_status("Interpolated (polyline)", "normal")
        self.p.update()

    def flat_tangents(self):
        curve = self.preprocess_curve

        keyframes = self.keyframes
        segments = zip(keyframes[:-1], keyframes[1:])

        self.splines = []
        for (k1, k2) in segments:
            spline_obj = FlatTangent()
            spline_obj.update_curve(curve[k1 : k2 + 1])
            spline_obj.update(self.params)
            spline_obj.run()
            self.splines.append(spline_obj)

        self.update_metric_map()
        Scene().set_status("Interpolated (flat spline)", "normal")
        self.p.update()

    def cubic(self):
        curve = self.preprocess_curve

        keyframes = self.keyframes
        segments = zip(keyframes[:-1], keyframes[1:])

        self.splines = []
        for (k1, k2) in segments:
            spline_obj = OnePass()
            spline_obj.update_curve(curve[k1 : k2 + 1])
            spline_obj.update(self.params)
            spline_obj.run()
            self.splines.append(spline_obj)

        self.update_metric_map()
        Scene().set_status("Interpolated (cubic spline)", "normal")
        self.p.update()

    def reset_simplification(self):
        self.mc_handle = ""
        self.preprocess_curve = []
        self.keyframes = []
        self.splines = []
        Scene().set_status("keyframes deleted", "normal")
        self.p.update()

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Metrics

    def update_metric_map(self):

        # Make a curve from spline pieces
        approx = []
        approx_length = 0
        for spline in self.splines:
            approx += spline.interps_nearest()[:-1]
            approx_length += spline.get_length()
        approx += [self.splines[-1].interps_nearest()[-1]]

        # Measure errors
        a = [v for v in self.preprocess_curve]
        b = [v for v in approx]
        self.metric_map.m["distance"]["normOne"] = Distance.normOne(a, b)
        self.metric_map.m["distance"]["normTwo"] = Distance.normTwo(a, b)
        self.metric_map.m["distance"]["normInf"] = Distance.normInf(a, b)
        self.metric_map.m["distance"]["meanSqu"] = Distance.meanSqu(a, b)

        # Measure lengths
        self.metric_map.m["length"]["original"] = 0  # TODO implement data length
        self.metric_map.m["length"]["simplified"] = approx_length

        # Record numbers of critical points
        c_org = len(Critical.local_minima(a) + Critical.local_maxima(a))
        c_simp = len(Critical.local_minima(b) + Critical.local_maxima(b))
        self.metric_map.m["critical"]["original"] = c_org
        self.metric_map.m["critical"]["simplified"] = c_simp

        # Save data to map
        m = self.metric_map_handle
        for key in m.keys():
            inner_m = m[key]
            for inner_key in inner_m:
                v = self.metric_map.m[key][inner_key]
                inner_m[inner_key].setText("%s, %2.2f" % (inner_key, v))

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Display stuff

    def toggle_display_fn(self, item):
        def fn(*args):
            self.display[item] = self.display[item] == False
            self.p.update()

        return fn

    def toggle_display_type(self, index):
        self.display = Graph.draw_type[index]
        self.p.update()
예제 #6
0
class Timeline(AppElementSingle):
    def __init__(self):
        self.current_frame = 1
        self.start_frame = 1
        self.end_frame = 2
        self.time_last = -1
        self.frame_rate = AppSettings.standard_fps
        self.playing = False

    # ------------------------------------------------------------------------ #
    # App element default

    def setup_ui(self):
        x = 30
        frame_range = (self.start_frame, self.end_frame)

        self.fps = Widgets.int_roller(self.p, self.frame_rate, self.update_frame_rate, AppSettings.fps_range, xy=(x, 2))
        x += 52

        Widgets.button(self.p, "<-", self.play_backward, (x, 0))
        x += 90
        Widgets.button(self.p, "||", self.play_stop, (x, 0))
        x += 90
        Widgets.button(self.p, "->", self.play_forward, (x, 0))
        x += 100

        self.frame_roller = Widgets.int_roller(
            self.p, self.current_frame, self.set_current_frame, frame_range, xy=(x, 2)
        )
        x += 50

        self.ticker = Widgets.ticker(self.p, self.tick)
        self.update_frame_rate()

    def keyPressEvent(self, e):
        if self.p.query_key("-", e.key()):
            self.tweak(-1)

        if self.p.query_key("=", e.key()):
            self.tweak(+1)

    def init_panel(self):
        self.p = Panel(AppSettings.timeline_size)
        self.p.draw = self.draw
        self.p.setup_ui = self.setup_ui
        self.p.init()

        # turns off default IO
        self.p.mouseReleaseEvent = self.empty
        self.p.mouseMoveEvent = self.empty
        self.p.wheelEvent = self.empty
        self.p.keyPressEvent = self.keyPressEvent
        return self.p

    def draw(self, d):
        sd = -AppSettings.timeline_size[0] * 0.4
        ed = +AppSettings.timeline_size[0] * 0.4
        hh = AppSettings.timeline_size[1] * 0.1
        sf = self.start_frame
        ef = self.end_frame

        def lerp_drawing_range(frame):
            t = (frame - sf) / float(ef - sf)
            return sd + t * (ed - sd)

        # Timeline graph
        d.fill((0, 0, 0), a=0)
        d.stroke(Colors.null, a=125)
        d.line((sd - 10, 0), (ed + 10, 0))
        for frame in range(sf, ef + 1):
            x = lerp_drawing_range(frame)
            d.line((x, -hh), (x, +hh))

        # Current time marker
        d.fill(Colors.time_marker, a=200)
        d.stroke((0, 0, 0), a=0)
        x = lerp_drawing_range(self.current_frame)
        d.box((x, 0), (10, hh * 2))

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Widget

    def tick(self, *args):
        self.current_frame += 1 * self.playing
        self.bound_current_frame()
        self.frame_roller.setValue(self.current_frame)
        Scene().evaluate_frame(self.current_frame)
        Animation.evaluate_frame(self.current_frame)
        self.p.update()

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Menu

    def play_forward(self):
        self.playing = 1

    def play_stop(self):
        self.playing = 0

    def play_backward(self):
        self.playing = -1

    def update_frame_rate(self):
        self.frame_rate = self.fps.value()
        self.ticker.start(1000.0 / self.frame_rate)

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Playback interface

    def set_frame_range(self, s, f):
        self.start_frame = s
        self.end_frame = f
        self.n_frames = f - s + 1
        self.frame_roller.setMinimum(s)
        self.frame_roller.setMaximum(f)

    def set_current_frame(self, frame):
        self.current_frame = frame
        self.bound_current_frame()
        Scene().evaluate_frame(self.current_frame)
        self.p.update()

    def bound_current_frame(self):
        if self.current_frame > self.end_frame:
            self.current_frame = self.start_frame

        if self.current_frame < self.start_frame:
            self.current_frame = self.end_frame

    def tweak(self, change):
        self.current_frame += change
        self.bound_current_frame()
        Scene().evaluate_frame(self.current_frame)
        self.p.update()
예제 #7
0
class Scene(AppElementSingle):

    # ------------------------------------------------------------------------ #
    # --

    def __init__(self):
        self.toons = []
        self.toon_filepaths = []
        self.display = {"axis": True}

    def setup_ui(self):

        w = 0

        menu = [
            ("Load Scene", self.load_scene, False, False),
            ("Save Scene", self.save_scene, False, False),
            ("Load Mocap", self.load_mocap, False, False),
        ]
        Widgets.button_menu(self.p, "File", menu, (w, 0))
        w += 100

        menu = [
            ("Load", self.load_toon, False, False),
            ("Examine", self.examine_toon, False, False),
            ("Offset", self.rotate_toon, False, False),
            ("Scale", self.scale_toon, False, False),
        ]
        Widgets.button_menu(self.p, "Character", menu, (w, 0))
        w += 120

        menu = [("Axis", self.toggle_display_fn("axis"), True, True)]
        Widgets.button_menu(self.p, "Display", menu, (w, 0))
        w += 120

        self.status_bar = Widgets.label(
            self.p, " Status", (10, AppSettings.scene_size[1] - 20), w=AppSettings.scene_size[0] - 20
        )
        self.set_status("loaded %s" % self.toon_filepaths, "normal")

    def mousePressEvent(self, e):
        if self.p.query_button(2, e.buttons()):
            self.command_select(e.x(), e.y())

    def keyPressEvent(self, e):

        # Character hotkeys
        if self.p.query_key("left", e.key()):
            self.command_modify(Joint.Move_L)
        if self.p.query_key("right", e.key()):
            self.command_modify(Joint.Move_R)
        if self.p.query_key("down", e.key()):
            self.command_modify(Joint.Move_D)
        if self.p.query_key("up", e.key()):
            self.command_modify(Joint.Move_U)
        if self.p.query_key("a", e.key()):
            self.command_modify(Joint.Rot_L)
        if self.p.query_key("d", e.key()):
            self.command_modify(Joint.Rot_R)

        # Interface
        if self.p.query_key("e", e.key()):
            self.examine_toon()
        if self.p.query_key("esc", e.key()):
            Executable.quit()
        if self.p.query_key("-", e.key()):
            Timeline().tweak(-1)
        if self.p.query_key("=", e.key()):
            Timeline().tweak(+1)

        # Camera
        if self.p.query_key("x", e.key()):
            self.p.toggle_camera_lock()
        if self.p.query_key("c", e.key()):
            self.p.store_camera()
        if self.p.query_key("v", e.key()):
            self.p.recover_camera()

    def init_panel(self):
        self.p = Panel(AppSettings.scene_size)
        self.p.draw = self.draw
        self.p.setup_ui = self.setup_ui
        self.p.init()

        self.p.mousePressEvent = self.mousePressEvent
        self.p.keyPressEvent = self.keyPressEvent
        return self.p

    def draw(self, d):
        if self.display["axis"]:
            d.axis()
        for t in self.toons:
            t.draw(d)

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Toon API

    def add_toon_from_file(self, filepath):
        f = open(filepath, "r")
        toon = json.loads(f.read())
        f.close()

        t = Toon()
        for joint in toon["joints"]:
            joint_obj = t.joint_add(joint["name"], joint["parent"], joint["type"], joint["length"], joint["flip"])
            joint_obj.rotation_offset = joint["offset"]
            joint_obj.pos_scaling = joint["pos_s"]

        # Update cache and scene
        self.toon_filepaths.append(filepath)
        self.toons.append(t)
        self.p.update()

    def load_toon(self):
        filepath = Dialogs.open_file(self.p, AppSettings.toon_dir)[0]
        if os.path.isfile(filepath):
            self.add_toon_from_file(filepath)
        else:
            Scene().set_status("File selected is not valid", "error")

    def examine_toon(self):
        toon_index, selected = self.get_selected()
        if selected != None:
            Executable.spawn_window("Motion", AppSettings.anim_size, Animation(toon_index, selected.name).init_panel())

    def rotate_toon(self):
        for t in self.toons:
            if t.selection_active():
                v = Dialogs.int(self.p, "Rotate", "degrees anticlockwise")
                if v != None:
                    t.modify_rotation_offset(v)

    def scale_toon(self):
        for t in self.toons:
            if t.joint_selected() != None:
                v = Dialogs.float(self.p, "Scale", "scale character")
                if v != None:
                    t.modify_position_scaling(v)

    def get_selected(self):
        for i in range(len(self.toons)):
            t = self.toons[i]
            if t.joint_selected() != None:
                return i, t.joint_selected()
        return -1, None

    def curve_data_on_selected(self):
        for t in self.toons:
            if t.selection_active():
                return t.sele.get_motion_info()
        return None

    def command_select(self, x, y):
        already_found = False
        for t in self.toons:
            if t.selection_cast(x, y, already_found):
                already_found = True
        self.p.update()

    def command_modify(self, t_type):
        for t in self.toons:
            t.modify_interactive(t_type)
        self.p.update()

    def command_add_motion(self, character, name, target, curve_key):
        mobj = MocapHandle().generate_motion_obj(curve_key)

        joint_obj = self.toons[character].joint_by_name(name)
        joint_obj.add_retarget(target, mobj)
        self.p.update()

    def command_get_motion(self, character, name, target):
        joint_obj = self.toons[character].joint_by_name(name)
        if target in joint_obj.retargets:
            return joint_obj.retargets[target].motion
        else:
            Scene().set_status("Target not found in joint", "warning")
            return None

    def command_delete_motion(self, character, name):
        joint_obj = self.toons[character].joint_by_name(name)
        joint_obj.clear_retargets()
        self.p.update()

    def evaluate_frame(self, frame):
        for t in self.toons:
            t.evaluate_frame(frame)
        self.p.update()

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Loading and saving

    def load_mocap(self):
        MocapHandle().load_mocap()

    def save_scene(self):

        # cache character info and motions
        character_motion_maps = []
        character_infos = []
        for t in self.toons:
            motion_maps = {}
            character_info = {}
            for joint in t.joint_list():
                motion_maps[joint.name] = joint.generate_map_from_retargets()
                character_info[joint.name] = joint.get_info()

            character_motion_maps.append(motion_maps)
            character_infos.append(character_info)

        # organize into scene file
        scene_file = {
            "mocap": MocapHandle().last_loaded,
            "character_filepaths": self.toon_filepaths,
            "character_info": character_infos,
            "character_motion_maps": character_motion_maps,
        }

        # save
        filepath = Dialogs.save_file(self.p, AppSettings.save_dir)[0]
        f = open(filepath, "w")
        f.write(json.dumps(scene_file, sort_keys=True, indent=4, separators=(",", ": ")))
        f.close()

    def load_scene(self, filepath=""):
        if filepath == "":
            filepath = Dialogs.open_file(self.p, AppSettings.save_dir)[0]
        if not os.path.isfile(filepath):
            Scene().set_status("file selected is not valid.", "warning")
            return

        # open
        f = open(filepath, "r")
        scene_file = json.loads(f.read())
        f.close()

        # Load mocap (if it was there)
        if scene_file["mocap"] != "":
            MocapHandle().load_mocap("%s/%s" % (AppSettings.demuddle_dir, scene_file["mocap"]))

        # Load characters.
        for fp in scene_file["character_filepaths"]:
            self.add_toon_from_file("%s/%s" % (AppSettings.demuddle_dir, fp))

        # Apply info and motion onto characters
        cindex = 0
        for cindex in range(len(scene_file["character_motion_maps"])):
            motion_maps = scene_file["character_motion_maps"][cindex]
            character_infos = scene_file["character_info"][cindex]

            for joint_name in motion_maps.keys():
                character_info = character_infos[joint_name]
                motion_map = motion_maps[joint_name]
                joint_obj = self.toons[cindex].joint_by_name(joint_name)
                joint_obj.set_info(character_info)
                joint_obj.generate_retargets_from_map(motion_map)
            cindex += 1

        self.p.update()

    #
    # ------------------------------------------------------------------------ #

    # ------------------------------------------------------------------------ #
    # Other

    def toggle_display_fn(self, item):
        def fn(*args):
            self.display[item] = self.display[item] == False
            self.p.update()

        return fn

    def set_status(self, message, message_type):
        self.status_bar.setText(" Status: %s" % message)
        style = ""
        if message_type == "normal":
            style = "QLabel { background-color : #1573BD; color : white; }"
        elif message_type == "warning":
            style = "QLabel { background-color : #F6AC41; color : white; }"
        elif message_type == "error":
            stlye = "QLabel { background-color : #DE3B3C; color : white; }"
        else:
            pass
        self.status_bar.setStyleSheet(style)
예제 #8
0
class Dungeon(Element):
    """класс подземелья"""
    def __init__(self):
        super().__init__()

        self.unused_keys = []
        self.first = True
        self.rooms = {}
        self.enemies = []
        self.objects = []
        self.base = []
        self.entities = []
        self.buttons = []
        self.user_name = ''
        self.con = sqlite3.connect(DATABASE)

        self.background, self.top_panel, self.bottom_panel = None, None, None

        self.player = Player((1, 1), 5, 5, 1, 1, 3, 3, 0, 255)
        self.current_room = 1
        self.turn = 1

        self.change_room(1)

    def new(self):
        self.unused_keys = []
        self.rooms = {}
        self.enemies = []
        self.objects = []
        self.base = []
        self.entities = []
        self.buttons = []
        self.user_name = ''
        self.first = True

        self.background, self.top_panel, self.bottom_panel = None, None, None

        self.player = Player((1, 1), 5, 5, 1, 1, 3, 3, 0, 255)
        self.current_room = 1
        self.turn = 1

        self.change_room(1)

    def change_room(self, num):  # смена комнаты, в которой находится игрок
        self.enemies = []
        self.objects = []
        self.base = []

        if num not in self.rooms.keys():
            # если следующей комнаты не существует
            self.generate_level(num)
        else:
            self.enemies = self.rooms[num].enemies
            self.objects = self.rooms[num].objects

        if self.first:
            # если запустили первый раз
            self.player.position = (1, 1)
            self.first = False
        elif num > self.current_room:  # смотрим, откуда пришел игрок
            self.player.position = self.rooms[num].enter[1], \
                                   self.rooms[num].enter[0]
        else:
            self.player.position = self.rooms[num].exit_[1], \
                                   self.rooms[num].exit_[0]

        self.current_room = num
        self.entities = [self.player, *self.enemies]
        self.load_room(self.current_room)

    def load_room(self, num_of_room):  # загрузка комнаты на экран
        self.base = []
        level = self.rooms[num_of_room].structure()
        empty = Image.open('Sprites/ground/idle/00.png')
        wall = Image.open('Sprites/wall/idle/00.png')
        background = Image.new('RGB',
                               (len(level[0]) * TILE, len(level) * TILE),
                               (255, 255, 255))
        # собираем из маленьких изображений пустых клетов и стен
        # одно большое изображение поля чтобы потом отображать только его
        for i in range(len(level)):
            for k in range(len(level[i])):
                if level[i][k] == 'W':
                    self.base.append(Wall((k, i)))
                    background.paste(wall, (k * TILE, i * TILE))
                else:
                    self.base.append(Empty((k, i)))
                    background.paste(empty, (k * TILE, i * TILE))
        self.background = pygame.image.fromstring(background.tobytes(),
                                                  background.size,
                                                  background.mode)

        self.top_panel = Panel(self.player, 0)  # создаем верхнюю
        self.bottom_panel = Panel(None, 550)  # и нижнюю панели
        self.buttons = [  # создаем кнопки
            Button('game/panel/exit', (550, 10), 'menu'),
            Button('game/panel/inventory', (450, 10), 'inventory'),
            Button('game/panel/save', (500, 10), 'save'),
        ]

    def generate_enemies(self, room):
        patterns = [
            ['blue', 2, 2, 1, 1, 2, 2],
            ['green', 3, 3, 1, 2, 2, 2],
            ['red', 3, 3, 2, 2, 2, 2],
            ['purple', 4, 4, 2, 3, 3, 3],
        ]
        enemy_number = [0, 0, 0, 0]
        if room == 1:
            a = random.randint(2, 4)
            enemy_number = [a, 4 - a, 0, 0]
        elif 2 <= room <= 5:
            a = random.randint(3, 4)
            enemy_number = [4 - a, a, 0, 0]
        elif 6 <= room <= 9:
            a = random.randint(2, 3)
            enemy_number = [1, a, 3 - a, 0]
        elif 10 <= room <= 14:
            a = random.randint(3, 5)
            enemy_number = [0, 5 - a, a, 0]
        elif 15 <= room <= 20:
            a = random.randint(3, 4)
            enemy_number = [0, 4 - a, a, 1]
        elif room > 20:
            a = random.randint(4, 5)
            enemy_number = [0, 0, 6 - a, a]

        return [
            patterns[i] for i in range(len(patterns))
            for _ in range(enemy_number[i])
        ]

    def generate_level(self, num):  # генерация уровня игры (карты)
        closed_cells = [self.player.position]
        enter = (0, 0)

        if num != 1:
            enter = self.rooms[num - 1].enter_from_exit()

        enemies_options = self.generate_enemies(num)
        for i in range(len(enemies_options)):  # генерация врагов
            x, y = random.randint(2, 9), random.randint(2, 8)
            while (x, y) in closed_cells:
                x, y = random.randint(2, 9), random.randint(2, 8)
            self.enemies.append(Enemy((x, y), *enemies_options[i]))
            closed_cells.append((x, y))

        for i in range(random.randint(6, 7)):  # генерация коробок
            x, y = random.randint(2, 8), random.randint(2, 7)
            while (x, y) in closed_cells:
                x, y = random.randint(2, 8), random.randint(2, 7)
            self.objects.append(Box((x, y)))
            closed_cells.append((x, y))

        a, b = (0, 2)
        for i in range(random.randint(a, b)):  # генерация зельев для здоровья
            x, y = random.randint(1, 9), random.randint(2, 8)
            while (x, y) in closed_cells:
                x, y = random.randint(1, 9), random.randint(2, 8)
            self.objects.append(Chest((x, y), 'potion', 'green'))
            closed_cells.append((x, y))
        exit_ = random.choice([(random.randint(2, 8), 11),
                               (9, random.randint(2, 9))])

        if num > 6:  # генерация зельев для повышения силы или количества ходов
            if not random.randint(0, 2):
                x, y = random.randint(1, 9), random.randint(2, 8)
                while (x, y) in closed_cells:
                    x, y = random.randint(1, 9), random.randint(2, 8)
                self.objects.append(
                    Chest((x, y), 'potion', random.choice(['red', 'blue'])))
                closed_cells.append((x, y))

        if not random.randint(0, 2) and len(self.unused_keys) < 6:
            # генерация двери, если неиспользованных ключей меньше 6
            door_color = random.choice(['red', 'blue', 'green'])

            x, y = random.randint(1, 9), random.randint(1, 8)
            while (x, y) in closed_cells:
                x, y = random.randint(2, 9), random.randint(2, 8)
            self.objects.append(Chest((x, y), 'key', door_color))
            self.unused_keys.append(door_color)

        if not random.randint(0, 2) and self.unused_keys:
            # добавится ли дверь в текущую комнату
            self.objects.append(
                Door((exit_[1], exit_[0]),
                     self.unused_keys.pop(
                         random.randint(0,
                                        len(self.unused_keys) - 1))))

        self.rooms[num] = Room(exit_, self.enemies, self.objects, num, enter)

    def load(self, user_name):  # загрузка игры с базы
        cur = self.con.cursor()
        self.user_name = user_name

        player = cur.execute(f"""SELECT room_num, 
            hit_points, max_hit_points, 
            damage, max_damage, 
            action_points, max_action_points,
            posX, posY, experience, max_experience FROM users 
            WHERE user_name = '{user_name}'""").fetchone()
        # все харастеристикик игрока

        self.unused_keys = list(
            map(
                lambda i: i[0],
                cur.execute(f"""SELECT type
            FROM inventory 
            WHERE user = '******' AND used = 'False'""")))

        self.player = Player((player[-4], player[-3]), *player[1:-4],
                             *player[-2:])  # игрок

        self.player.inventory = list(
            map(
                lambda i: i[0],
                cur.execute(f"""SELECT
            type FROM inventory 
            WHERE user = '******' AND used = 'True'""")))
        self.current_room = player[0]

        rooms = cur.execute(f"""SELECT id, number, enter_posX, 
                    enter_posY, exit_posX, exit_posY FROM rooms
                    WHERE user = '******'""").fetchall()

        self.rooms = {}
        for room_id, number, *positions in rooms:  # все комнаты подземелья
            enemies = cur.execute(f"""SELECT color, hit_points, 
                        max_hit_points,
                        action_points, max_action_points, 
                        damage, max_damage, posX, posY FROM entities
                        WHERE room_id = {room_id}""").fetchall()

            list_of_enemies = []
            list_of_objects = []
            # все враги на карте
            for color, hit, m_hit, act, m_act, dam, m_dam, x, y in enemies:
                list_of_enemies.append(
                    Enemy((x, y), color, hit, m_hit, dam, m_dam, act, m_act))

            objects = cur.execute(f"""SELECT type, posX, posY, inside, 
                color, stage FROM objects
                WHERE room_id = {room_id}""").fetchall()

            for type_, x, y, inside, color, stage in objects:
                # все объекты на карте
                if type_ == 1:  # коробки
                    list_of_objects.append(Box((x, y)))
                elif type_ == 2:  # сундуки
                    list_of_objects.append(
                        Chest((x, y), *reversed(inside.split('_'))))
                    if stage:
                        list_of_objects[-1].touch()
                else:  # двери
                    list_of_objects.append(Door((x, y), color))

            self.rooms[number] = Room(positions[-2:], list_of_enemies,
                                      list_of_objects, number, positions[:2])

        self.enemies = self.rooms[self.current_room].enemies
        self.entities = [self.player, *self.enemies]
        self.objects = self.rooms[self.current_room].objects
        self.load_room(player[0])

    def save_room(self, n):  # сохранение 1 комнаты в базе
        cur = self.con.cursor()
        room_id = cur.execute(f"""SELECT id FROM rooms 
            WHERE number = {n} and user = '******'""").fetchone()[0]
        room = self.rooms[n]
        for enemy in room.enemies:
            if enemy.alive:
                cur.execute(f"""INSERT INTO entities(hit_points, 
                max_hit_points,
                action_points, max_action_points, 
                damage, max_damage, posX, posY, room_id, color)
                values({enemy.hit_points[0]}, {enemy.hit_points[1]},
                {enemy.action_points[0]}, {enemy.action_points[1]},
                {enemy.damage[0]}, {enemy.damage[1]}, 
                {enemy.position[0]}, {enemy.position[1]}, {room_id}, 
                '{enemy.color}')""")

        for obj in room.objects:  # объекты комнаты
            if obj.name == 'box':
                cur.execute(f"""INSERT INTO objects(type, posX, posY, 
                room_id) 
                values(1, {obj.position[0]}, 
                {obj.position[1]}, {room_id})""")
            elif obj.stage == 0:  # если объект активен
                if obj.name == 'chest':
                    cur.execute(f"""INSERT INTO objects(type, posX, 
                        posY, room_id, inside) values(2, {obj.position[0]}, 
                        {obj.position[1]}, {room_id}, '{obj.inside.name}')""")
                elif obj.name == 'door':
                    cur.execute(f"""INSERT INTO objects(type, posX, 
                        posY, room_id, color) values(3, {obj.position[0]}, 
                        {obj.position[1]}, {room_id}, '{obj.color}')""")
            elif obj.stage == 1 and obj.name == 'chest':

                if obj.name == 'chest':
                    cur.execute(f"""INSERT INTO objects(type, posX, 
                        posY, room_id, inside, stage) 
                        values(2, {obj.position[0]}, 
                        {obj.position[1]}, {room_id}, 
                        '{obj.inside.name}', 1)""")
            self.con.commit()

    def update_base(self):
        # обновление базы (если имя пользователя уже вводилось)
        cur = self.con.cursor()
        cur.execute(f"""UPDATE users
                    SET room_num = {self.current_room},
                    hit_points = {self.player.hit_points[0]}, 
                    max_hit_points = {self.player.hit_points[1]}, 
                    action_points = {self.player.action_points[0]}, 
                    max_action_points = {self.player.action_points[1]},
                    damage = {self.player.damage[0]}, 
                    max_damage = {self.player.damage[1]}, 
                    posX = {self.player.position[0]}, 
                    posY = {self.player.position[1]}, 
                    experience = '{self.player.experience[0]}', 
                    max_experience = '{self.player.experience[1]}'
                    WHERE user_name = '{self.user_name}'""")
        self.con.commit()

        cur.execute("""DELETE FROM inventory 
        WHERE user = '******'""")  # удаление старого инвентаря

        for obj in self.player.inventory:
            cur.execute(f"""INSERT INTO inventory(user, type, used)
            values('{self.user_name}', '{obj}', 'True')""")

        for obj in self.unused_keys:
            cur.execute(f"""INSERT INTO inventory(user, type, used)
            values('{self.user_name}', '{obj}', 'False')""")

        self.con.commit()

        for n, room in self.rooms.items():
            # если в комнату заходили, то есть могли изменяться
            # положения врагов, объектов и тд
            room_id = cur.execute(f"""SELECT id FROM rooms 
                    WHERE number = {n} 
                    and user = '******'""").fetchone()[0]

            cur.execute(f"""DELETE FROM entities 
                    WHERE room_id = {room_id}""")

            self.con.commit()

            cur.execute(f"""DELETE FROM objects 
                    WHERE room_id = {room_id}""")

            self.con.commit()
            self.save_room(n)

    def save(self, user_name):  # функция сохранения базы
        cur = self.con.cursor()
        self.user_name = user_name if not self.user_name else self.user_name
        cur.execute(f"""INSERT INTO users(user_name, room_num, 
            hit_points, max_hit_points, 
            action_points, max_action_points,
            damage, max_damage, posX, posY, experience, max_experience)
            values('{self.user_name}', {self.current_room}, 
            {self.player.hit_points[0]}, {self.player.hit_points[1]}, 
            {self.player.action_points[0]}, {self.player.action_points[1]}, 
            {self.player.damage[0]},{self.player.damage[1]},
            {self.player.position[0]}, 
            {self.player.position[1]},
            '{self.player.experience[0]}', '{self.player.experience[1]}')""")
        # добавление нового пользователя со всеми характеристиками
        self.con.commit()

        for obj in self.player.inventory:  # добавление инвентаря игрока
            cur.execute(f"""INSERT INTO inventory(user, type, used)
            values('{self.user_name}', '{obj}', 'True')""")

        for obj in self.unused_keys:  # добавляются неиспользованные ключи
            cur.execute(f"""INSERT INTO inventory(user, type, used)
            values('{self.user_name}', '{obj}', 'False')""")

        for n, room in self.rooms.items():
            cur.execute(f"""INSERT INTO rooms(number, enter_posX, 
            enter_posY, exit_posX, exit_posY, user) 
            values({n}, {room.enter[0]}, {room.enter[1]}, 
            {room.exit_[0]}, {room.exit_[1]}, '{self.user_name}')""")
            self.con.commit()
            self.save_room(n)  # добавление каждой комнаты в базу

    def get(self, coordinates, diff=(0, 0)):
        """Возвращает объект по координатам"""
        for obj in [*self.entities, *self.objects, *self.base]:
            if obj.position == (coordinates[0] + diff[1],
                                coordinates[1] + diff[0]):
                if getattr(obj, 'alive', True):
                    return obj

    def player_move(self, button):
        """Движение игрока"""

        # словарь вида {кнопка: (смещение на X и Y)}
        buttons_keys = {
            pygame.K_LEFT: (0, -1),
            pygame.K_RIGHT: (0, 1),
            pygame.K_UP: (-1, 0),
            pygame.K_DOWN: (1, 0)
        }

        if any([
                i.animator.animation not in ['idle', 'die']
                for i in self.enemies
        ]):
            # если враги еще совершают какие-то действия, то игрок стоит
            return
        if self.player.animator.animation != 'idle':
            # если игрок совершает какое-то действие, то
            # мы не начинаем новое действие
            return
        if button not in buttons_keys.keys():
            return  # если нажали на неизвестную кнопку

        # проверяем на нахождение в телепорте
        self.player.interaction_teleport(self)
        # взаимодействуем с объектом
        obj = self.get(self.player.position, buttons_keys[button])
        obj_name = obj.name if hasattr(obj, 'name') else None
        self.player.interaction(self, buttons_keys[button])
        if obj_name == 'enemy' and not obj.alive:
            self.player.experience[0] += 2
            if self.player.experience[0] > self.player.experience[1]:
                config.NEXT_WINDOW = 'win'

    def enemies_move(self):
        """Движение врагов"""

        if self.player.animator.animation != 'idle':
            return
            # если игрок что-то делает, то враги не начинают новых действий

        options = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        res = []
        blocked_cells = []

        for enemy in self.enemies:
            if not enemy.alive:
                # текущий враг метрв, то переходим к следущему врагу
                continue

            if enemy.animator.animation != 'idle':
                res.append(True)
                # если враг уже совершает действие,
                # то переходим к следущему врагу
                continue

            checking = []
            for i in options:
                checking.append(
                    self.get(enemy.position, i).name in ('player', 'empty'))

            if not any(checking):  # если врагу некуда идти
                res.append(False)
                continue

            player_pos = self.player.position
            enemy_pos = enemy.position
            diff = (0, 0)
            if random.randint(0, 1):  # генерация ходов врага
                # враг пытается приблизиться к игроку
                if enemy_pos[0] != player_pos[0]:
                    diff = (0, -1) if enemy_pos[0] > player_pos[0] else (0, 1)
                elif enemy_pos[1] != player_pos[1]:
                    diff = (-1, 0) if enemy_pos[1] > player_pos[1] else (1, 0)
            else:
                if enemy_pos[1] != player_pos[1]:
                    diff = (-1, 0) if enemy_pos[1] > player_pos[1] else (1, 0)
                elif enemy_pos[0] != player_pos[0]:
                    diff = (0, -1) if enemy_pos[0] > player_pos[0] else (0, 1)

            while (enemy_pos[0] + diff[0],
                   enemy_pos[1] + diff[1]) in blocked_cells or \
                    self.get(enemy_pos, diff).name not in ('empty', 'player'):
                diff = options[random.randint(0, len(options) - 1)]

            blocked_cells.append(
                (enemy_pos[0] + diff[0], enemy_pos[1] + diff[1]))

            # добавляем результат взаимодействия с список
            res.append(enemy.interaction(self, diff))

        if not any(res):  # если у всех врагов закончились очки действий
            self.turn = 1  # передаем ход игроку
            for enemy in self.enemies:  # обновляем очки действий у врагов
                enemy.action_points[0] = enemy.action_points[1]

    def show(self, surf):
        """Отображение на поверхность"""
        if self.turn == 2:
            self.enemies_move()

        surf.blit(self.background, apply((0, 0)))  # отображаем поле
        for entity in self.entities:  # отображаем существ
            entity.show(surf)
        for obj in self.objects:  # отображаем объекты
            obj.show(surf)

        self.top_panel.show(surf)  # отображаем верхнюю
        self.bottom_panel.show(surf)  # и нижнюю панели

        for elem in self.buttons:  # отображаем кнопки
            elem.show(surf)

    def button_down(self, mouse_pos):
        """Нажатие мыши"""
        # получаем объект, на который нажали
        obj = self.get(
            (mouse_pos[0] // TILE, (mouse_pos[1] - PANEL_HEIGHT) // TILE))
        if isinstance(obj, Enemy) and obj.alive:  # если нажали на врага
            self.bottom_panel.change_target(obj)  # меняем цель нижней панели
        else:
            self.bottom_panel.change_target(None)

        for elem in self.buttons:  # проверяем нажатие на кнопки
            elem.button_down(mouse_pos)

    def key_down(self, key):
        """Нажатие на клавиатуру"""
        for elem in self.buttons:
            elem.key_down(key)
        if self.turn == 1:  # если ход игрока
            self.player_move(key)  # то вызываем функцию движения игрока