namerena-rs/maker-py/main.py

447 lines
14 KiB
Python

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_info_displays = {}
self.init_name_dispaly()
self.init_name_diy()
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_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:
# 捏起
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()