diff --git a/Cargo.toml b/Cargo.toml index 6a886bd..21d3bfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,7 @@ resolver = "2" members = [ "rust-namer", - "miner" -] + "miner"] # [miner.profile.release] # opt-level = 3 diff --git a/maker-py/.gitignore b/maker-py/.gitignore new file mode 100644 index 0000000..8fa78e4 --- /dev/null +++ b/maker-py/.gitignore @@ -0,0 +1,5 @@ +env +venv + +*__pycache__* + diff --git a/maker-py/camera.py b/maker-py/camera.py new file mode 100644 index 0000000..0b8d77a --- /dev/null +++ b/maker-py/camera.py @@ -0,0 +1,75 @@ +from typing import Tuple, Optional + +# from pyglet.gl import gl +from pyglet.math import Mat4, Vec3 +from pyglet.graphics import Group + + +class GroupCamera(Group): + """ + A camera by group + can be used by just added to your widget + """ + + def __init__( + self, + window, + order: int = 0, + parent: Optional[Group] = None, + view_x: Optional[int] = 0, + view_y: Optional[int] = 0, + zoom: Optional[float] = 1.0, + min_zoom: Optional[float] = 1.0, + max_zoom: Optional[float] = 1.0, + ): + super().__init__(order=order, parent=parent) + self._window = window + self._previous_view = None + + self._view_x = view_x or 0 + self._view_y = view_y or 0 + self._zoom = zoom or 1.0 + self.min_zoom = min_zoom or 1.0 + self.max_zoom = max_zoom or 1.0 + + @property + def view_x(self) -> int: + return self._view_x + + @view_x.setter + def view_x(self, value: int): + self._view_x = value + + @property + def view_y(self) -> int: + return self._view_y + + @view_y.setter + def view_y(self, value: int): + self._view_y = value + + @property + def zoom(self) -> float: + return min(max(self._zoom, self.min_zoom), self.max_zoom) + + @zoom.setter + def zoom(self, value: float): + self._zoom = value + + def reset(self): + self._view_x = 0 + self._view_y = 0 + self.zoom = 1 + + def set_state(self): + self._previous_view = self._window.view + + view = Mat4.from_translation(Vec3(self._view_x, self._view_y, 0)) + if self._zoom == 1.0: + self._window.view = view + else: + view = view.scale(Vec3(self._zoom, self._zoom, 1)) + self._window.view = view + + def unset_state(self): + self._window.view = self._previous_view diff --git a/maker-py/control.py b/maker-py/control.py new file mode 100644 index 0000000..db9490a --- /dev/null +++ b/maker-py/control.py @@ -0,0 +1,106 @@ +from typing import Callable, Union, Tuple, Dict, TYPE_CHECKING, TypeVar, Optional + +if TYPE_CHECKING: + from pyglet.text import Label + from pyglet.window import Window + from pyglet.sprite import Sprite + from pyglet.shapes import ShapeBase + from pyglet.gui.widgets import WidgetBase + + Repositionable = Union[ShapeBase, Label, Sprite, WidgetBase] # just for typing +else: + Window = TypeVar("Window") # for type checking + Repositionable = TypeVar("Repositionable") # for type checking + +Num = Union[int, float] + +PotitionTuple = Union[Tuple[Num, Num], Tuple[Num, Num, Num]] + +CallBackFunc = Callable[[Repositionable, int, int, Window], None] +CalculateFunc = Callable[[Repositionable, int, int, Window], PotitionTuple] + +CallBack = Union[CallBackFunc, CalculateFunc] +IndexType = Union[int, str] + +__all__ = [ + "RePositionFrame", +] + + +class RePositionFrame: + """A Frame Like Object that allows for repositioning of widgets + you can give A function and A widget/shape to let it reposition itself + when the function is called + + >>> import pyglet + >>> window = pyglet.window.Window(resizable=True) + >>> reposition_frame = pyglet.gui.frame.RePositionFrame(window) + >>> label = pyglet.text.Label("Hello World", x=0, y=0) + >>> b_label = pyglet.text.Label("Hello World with call back", x=0, y=0) + >>> def callback(obj, width, height, window): + >>> obj.x = width/3 + >>> obj.y = height/3 + >>> obj.text = f"Hello World with call back, width: {width}, height: {height}" + >>> reposition_frame.add_calculate_func(label, lambda obj, width, height, window: (width/2, height/2, 0)) + >>> reposition_frame.add_callback_func(b_label, callback) + >>> @window.event + >>> def on_draw(): + >>> window.clear() + >>> label.draw() + >>> b_label.draw() + >>> pyglet.app.run() + + """ + + def __init__(self, window): + window.push_handlers(self) + self.window = window + self.callback_dict: Dict[IndexType, Tuple[Repositionable, CallBackFunc]] = {} + self.calculate_dict: Dict[IndexType, Tuple[Repositionable, CalculateFunc]] = {} + + def add_callback_func( + self, obj: Repositionable, func: CallBackFunc, index: Optional[IndexType] = None + ) -> IndexType: + """Add A callback function to the frame + + :param obj: The object that will be repositioned + :param func: The function that will be called + :param index: The index of the object + """ + if index is None: + index = hash(obj) + self.callback_dict[index] = (obj, func) + return index + + def add_calculate_func( + self, + obj: Repositionable, + func: CalculateFunc, + index: Optional[IndexType] = None, + ) -> IndexType: + """Add A calculate function to the frame + + :param obj: The object that will be repositioned + :param func: The function that will be called + :param index: The index of the object + """ + if index is None: + index = hash(obj) + self.calculate_dict[index] = (obj, func) + return index + + def remove_callback_func(self, index: IndexType): + if index in self.callback_dict: + self.callback_dict.pop(index) + + def remove_calculate_func(self, index: IndexType): + if index in self.calculate_dict: + self.calculate_dict.pop(index) + + def on_resize(self, width: int, height: int): + """Call all the functions when the window is resized""" + for _, (obj, func) in self.callback_dict.items(): + func(obj, width, height, self.window) + + for _, (obj, func) in self.calculate_dict.items(): + obj.position = func(obj, width, height, self.window) # type: ignore diff --git a/maker-py/main.py b/maker-py/main.py new file mode 100644 index 0000000..9d929ae --- /dev/null +++ b/maker-py/main.py @@ -0,0 +1,209 @@ +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 +from pyglet.gl import glClearColor +from pyglet.shapes import Rectangle +from pyglet.graphics import Batch, Group + +from control import RePositionFrame + +from enum import IntEnum + +gray = (200, 200, 200) + + +class NumStatus(IntEnum): + """未被选中""" + + wait = 0 + + # 血量 + hp = 1 + # 攻击 + attack = 2 + # 防御 + defense = 3 + # 速度 + speed = 4 + # 敏捷 + agility = 5 + # 魔法 + magic = 6 + # 抗性 + resistance = 7 + # 智慧 + wisdom = 8 + + +class NumWidget: + def __init__(self, num: int, batch: Batch, group: Group, x: int, y: int) -> None: + self.x = x + self.y = y + font = load_font("黑体", 15) + font_height = font.ascent - font.descent + 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=group, + ) + self.background = Rectangle( + x=x, + y=y, + width=35, + height=font_height + 4, + color=gray, + batch=batch, + group=group, + ) + + 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 + + +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.name_info_displays = {} + self.init_name_dispaly() + self.init_name_diy() + + def init_name_diy(self) -> None: + """ + 初始化 名字自定义 + """ + # 0-255 + self.num_dict = {} + self.num_batch = Batch() + self.num_group = Group(parent=self.main_group, order=10) + # 从大到小 + for i in range(256): + widget = NumWidget( + num=i, batch=self.num_batch, group=self.num_group, x=40, y=50 + ) + self.num_dict[i] = widget + # 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[int]] = { + NumStatus.hp: [self.num_dict[i] for i in range(3, 7)], + 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(0, 3)), + *(self.num_dict[i] for i in range(7, 10)), + *(self.num_dict[i] for i in range(31, 256)), + ], + } + + def init_name_dispaly(self) -> None: + """ + 初始化 名字显示 这块内容 + """ + name_group = Group(parent=self.main_group) + 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() + self.main_batch.draw() + self.num_batch.draw() + + def start(self) -> None: + pyglet.app.run(interval=1 / 30) + + +if __name__ == "__main__": + window = MainWindow() + glClearColor(1, 1, 1, 1) + window.start()