import pyglet from pyglet.font import load as load_font from pyglet.text import Label from pyglet.gui import TextEntry from pyglet.window import Window, mouse from pyglet.gl import glClearColor from pyglet.shapes import Rectangle from pyglet.graphics import Batch, Group from control import RePositionFrame from enum import IntEnum import math gray = (200, 200, 200) class NumStatus(IntEnum): """未被选中""" wait = 8 # 血量 hp = 0 # 攻击 attack = 1 # 防御 defense = 2 # 速度 speed = 3 # 敏捷 agility = 4 # 魔法 magic = 5 # 抗性 resistance = 6 # 智慧 wisdom = 7 class NumWidget: def __init__(self, num: int, batch: Batch, group: Group, x: int, y: int) -> None: self._y = y self._x = x self._display = True font = load_font("黑体", 15) font_height = font.ascent - font.descent self.label_group = Group(parent=group, order=20) self.background_group = Group(parent=group, order=10) self.label = Label( x=x + 17, y=y + 7, color=(0, 0, 0, 255), text=f"{num}", font_name="黑体", font_size=15, width=35, height=font_height + 4, anchor_x="center", batch=batch, group=self.label_group, ) self.background = Rectangle( x=x, y=y, width=35, height=font_height + 4, color=gray, batch=batch, group=self.background_group, ) @property def value(self) -> int: return int(self.label.text) @value.setter def value(self, value: int) -> None: self.label.text = str(value) @property def display(self) -> bool: return self._display @display.setter def display(self, value: bool) -> None: self._display = value self.label_group.visible = value self.background_group.visible = value @property def x(self) -> int: return self._x @x.setter def x(self, value: int) -> None: self._x = value self.label.x = value + 17 self.background.x = value @property def y(self) -> int: return self._y @y.setter def y(self, value: int) -> None: self._y = value self.label.y = value + 7 self.background.y = value def aabb(self, x: int, y: int) -> bool: # 判断是否在范围内 width = 35 height = 20 return self.x <= x <= self.x + width and self.y <= y <= self.y + height def middle_widget(一: NumWidget, 二: NumWidget, 三: NumWidget) -> int: """返回中间值""" a, b, c = 一.value, 二.value, 三.value if a < b < c or c < b < a: return b if b < a < c or c < a < b: return a return c class MainWindow(Window): def __init__(self, *args, **kwargs): super().__init__( resizable=True, width=800, height=600, caption="Maker", vsync=True, *args, **kwargs, ) self.main_batch = Batch() self.main_group = Group() self.main_frame = RePositionFrame(self) self.on_middle = False self.middle_base = (0, 0) self.drag_speed = 0 self.drag_start = None self.name_data = [] self.name_info_displays = {} self.init_info() self.init_name_dispaly() self.init_name_diy() def init_info(self) -> None: """ 初始化信息显示 """ self.info_label = Label( x=20, y=self.height - 50, text="名字竞技场, 八围制造器 by shenjackyuanjie(点完导出看控制台)", multiline=False, font_name="黑体", font_size=20, batch=self.main_batch, group=self.main_group, color=(0, 0, 0, 255), ) self.output_button = Rectangle( x=400, y=200, width=100, height=50, color=(0, 0, 255, 200), batch=self.main_batch, group=self.main_group, ) self.output_label = Label( x=400 + 50, y=200 + 25, text="导出", width=100, height=50, multiline=False, anchor_x="center", font_name="黑体", font_size=20, color=(255, 255, 255, 255), batch=self.main_batch, group=self.main_group, ) def init_name_diy(self) -> None: """ 初始化 名字自定义 """ # 0-255 self.num_dict = {} self.num_group = Group(parent=self.main_group, order=10) # 用于覆盖掉 num 顶上多出来的部分 cover_group = Group(parent=self.main_group, order=20) # 滚动条数值 self.num_slide = 0 self.num_cover = Rectangle( x=37 + 8 * 65, y=self.height - 143, width=41, height=40, color=(0, 255, 255, 255), batch=self.main_batch, group=cover_group, ) self.main_frame.add_calculate_func( self.num_cover, lambda rec, width, height, window: (37 + 8 * 65, height - 143), ) # 从大到小 num_group = Group(parent=self.num_group, order=10) for i in range(256): num_name = NumWidget( num=i, batch=self.main_batch, group=num_group, x=40, y=50 ) self.num_dict[i] = num_name self.num_hints = [] # 每个部分的取值提示 font = load_font("黑体", 15) font_height = font.ascent - font.descent num_hint_group = Group(parent=self.main_group, order=20) # hp: 3~6 len = 4 # 要覆盖住 4 个数字 self.num_hints.append( Rectangle( x=40 - 3, y=self.height - (173 + 30 * 6), width=41, height=(font_height + 4 + 5) * 4, # 浅蓝色背景 color=(0, 0, 255, 100), batch=self.main_batch, group=num_hint_group, ) ) # 剩下 7 个, 每个都是中间 for x in range(1, 8): self.num_hints.append( Rectangle( x=40 - 3 + (65 * x), y=self.height - (173 + 30), width=41, height=font_height + 4 + 5, # 浅蓝色背景 color=(0, 0, 255, 100), batch=self.main_batch, group=num_hint_group, ) ) # 0-9 sorted # 取前9个拿到血量这边 # index 3~6 之和 + 154 = 血量 # index 10~12 中值 + 36 = 攻击 # index 13~15 中值 + 36 = 防御 # index 16~18 中值 + 36 = 速度 # index 19~21 中值 + 36 = 敏捷 # index 22~24 中值 + 36 = 魔法 # index 25~27 中值 + 36 = 抗性 # index 28~30 中值 + 36 = 智慧 self.display_dict: dict[NumStatus, list[NumWidget]] = { NumStatus.hp: [self.num_dict[i] for i in range(0, 10)], NumStatus.attack: [self.num_dict[i] for i in range(10, 13)], NumStatus.defense: [self.num_dict[i] for i in range(13, 16)], NumStatus.speed: [self.num_dict[i] for i in range(16, 19)], NumStatus.agility: [self.num_dict[i] for i in range(19, 22)], NumStatus.magic: [self.num_dict[i] for i in range(22, 25)], NumStatus.resistance: [self.num_dict[i] for i in range(25, 28)], NumStatus.wisdom: [self.num_dict[i] for i in range(28, 31)], NumStatus.wait: [self.num_dict[i] for i in range(31, 256)], } self.update_num_display() def update_num_display(self) -> None: # sort hp self.display_dict[NumStatus.hp].sort(key=lambda x: x.value) # sort wait self.display_dict[NumStatus.wait].sort(key=lambda x: x.value) for status, widgets in self.display_dict.items(): num_count = 0 if status == NumStatus.wait: continue for widget in widgets: widget.x = 40 + (65 * status.value) widget.y = self.height - (170 + 30 * num_count) num_count += 1 # wait 的单独处理, 因为有滚动条 num_count = 0 for widget in self.display_dict[NumStatus.wait]: widget.x = 40 + (65 * NumStatus.wait.value) widget.y = self.height - (170 + 30 * num_count) + self.num_slide # 如果太高了, 就不显示了 if widget.y > self.height - 200: # 给我不显示啊啊啊啊啊啊 widget.display = False else: widget.display = True num_count += 1 # 计算数据 hp = sum(widget.value for widget in self.display_dict[NumStatus.hp][3:6]) + 154 attack = middle_widget(*self.display_dict[NumStatus.attack]) + 36 defense = middle_widget(*self.display_dict[NumStatus.defense]) + 36 speed = middle_widget(*self.display_dict[NumStatus.speed]) + 36 agility = middle_widget(*self.display_dict[NumStatus.agility]) + 36 magic = middle_widget(*self.display_dict[NumStatus.magic]) + 36 resistance = middle_widget(*self.display_dict[NumStatus.resistance]) + 36 wisdom = middle_widget(*self.display_dict[NumStatus.wisdom]) + 36 gather = sum( (int(hp / 3), attack, defense, speed, agility, magic, resistance, wisdom) ) self.name_data = [attack, defense, speed, agility, magic, resistance, wisdom, hp] self.name_info_displays[ "label" ].text = f"HP|{hp} 攻|{attack} 防|{defense} 速|{speed} 敏|{agility} 魔|{magic} 抗|{resistance} 智|{wisdom} 八围:{gather}" # 更新提示框 # hp 提示框是固定的 self.num_hints[0].y = self.height - (173 + 30 * 6) # 剩下的需要先判断那个是中间的 for i in range(1, 8): data = sorted(enumerate(x.value for x in self.display_dict[NumStatus(i)]), key=lambda x: x[1]) middle_index = data[1][0] self.num_hints[i].y = self.height - (173 + 30 * middle_index) def init_name_dispaly(self) -> None: """ 初始化 名字显示 这块内容 """ name_group = Group(parent=self.main_group, order=30) self.name_info_displays["group"] = name_group font = load_font("黑体", 20) font_height = font.ascent - font.descent name_rec = Rectangle( x=20, y=self.height - 135, width=600, # 在 callback 中定义 height=font_height, # 颜色: 灰色 color=gray, batch=self.main_batch, group=name_group, ) name_info_label = Label( x=25, y=self.height - 127, text="HP|{} 攻|{} 防|{} 速|{} 敏|{} 魔|{} 抗|{} 智|{} 八围:{}", width=400, multiline=False, font_name="黑体", font_size=15, color=(0, 0, 0, 255), batch=self.main_batch, group=name_group, ) name_entry = TextEntry( x=40, y=self.height - 100, width=200, text="x@x", # 灰色背景 color=(*gray, 255), text_color=(0, 0, 0, 255), batch=self.main_batch, group=name_group, ) def rec_callback(rec, width: int, height: int, window: Window): # rec.x = 20 rec.y = height - 135 self.main_frame.add_callback_func(name_rec, rec_callback) self.main_frame.add_calculate_func( name_info_label, lambda obj, width, height, window: (25, height - 127, 0), ) self.main_frame.add_calculate_func( name_entry, lambda obj, width, height, window: (40, height - 100), ) self.push_handlers(name_entry) self.name_info_displays["rec"] = name_rec self.name_info_displays["label"] = name_info_label self.name_info_displays["entry"] = name_entry def on_draw(self) -> None: self.clear() if self.on_middle: # 正在滑动 self.update_slide(self.drag_speed) self.main_batch.draw() def update_slide(self, dy: int) -> None: # 保证不会太下 if (self.num_slide + dy) < 0: self.num_slide = 0 self.update_num_display() return # 再保证不会太上 num_len = len(self.display_dict[NumStatus.wait]) if (self.num_slide + dy) > (30 * num_len - 100): self.num_slide = 30 * num_len - 100 self.update_num_display() return self.num_slide += dy self.update_num_display() def on_mouse_scroll(self, x, y, scroll_x, scroll_y): self.update_slide(int(scroll_y) * -10) def on_mouse_press(self, x, y, button, modifiers): self.middle_base = (x, y) if not button & mouse.MIDDLE: # 中键 self.on_middle = False if button & mouse.LEFT: # 捏起 if (x, y) in self.output_button: # 导出 print("导出") for status, widgets in self.display_dict.items(): if status == NumStatus.wait: continue print( f"{status.name}: {', '.join(str(x.value) for x in widgets)}" ) name = self.name_info_displays["entry"].value print("名字: ", name) # +diy[50,51,90,130,150,70,89,210] print(f"{name}+diy{self.name_data}") return for idx, widget in self.num_dict.items(): if widget.aabb(x, y): self.drag_start = idx # print(f"捏起 {idx}") break def on_mouse_release(self, x, y, button, modifiers): self.on_middle = False if button & mouse.LEFT: find = [] if self.drag_start: # 有开始目标 for idx, target_widget in self.num_dict.items(): if target_widget.aabb(x, y): if idx == self.drag_start: # 如果是自己, 就不做任何操作 continue # 搜索对面那个, 修改双方显示内容 # 交换 value find = [self.drag_start, idx] break if find: print(f"交换 {find}") ( self.num_dict[find[0]].value, self.num_dict[find[1]].value, ) = find[1], find[0] # 交换键 self.num_dict[find[0]], self.num_dict[find[1]] = ( self.num_dict[find[1]], self.num_dict[find[0]], ) self.drag_start = None self.update_num_display() def on_mouse_leave(self, x, y): self.on_middle = False def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): if dy != 0 and y != self.middle_base[1] and buttons & mouse.MIDDLE: if not self.on_middle: self.middle_base = (x, y) self.drag_speed = 0 self.on_middle = True return drag_y = y - self.middle_base[1] # 取个对数, 保证不会太快 self.drag_speed = int(drag_y) if self.drag_start: # 拖动 self.num_dict[self.drag_start].x = x - 17 self.num_dict[self.drag_start].y = y - 7 def on_resize(self, width, height): super().on_resize(width, height) self.update_num_display() def start(self) -> None: pyglet.app.run(interval=1 / 30) if __name__ == "__main__": window = MainWindow() glClearColor(1, 1, 1, 1) window.start()