Compare commits

..

No commits in common. "ad83def45e737aac0053e007de8db51b719dac1c" and "3629bcf163af342f6718eb3e09b3999075231d96" have entirely different histories.

13 changed files with 135 additions and 272 deletions

View File

@ -1,5 +1,3 @@
<div style="text-align: center;">
# Difficult Rocket # Difficult Rocket
中文 | [English](./docs/README-en.md) 中文 | [English](./docs/README-en.md)
@ -121,8 +119,6 @@ setuptools-rust >= 1.6.0
- [@Billchyi](https://github.com/Billchyi) : 文档矫正 - [@Billchyi](https://github.com/Billchyi) : 文档矫正
- [@MSDNicrosoft](https://github.com/MSDNicrosoft) : 优化代码 - [@MSDNicrosoft](https://github.com/MSDNicrosoft) : 优化代码
</div>
## 相关链接 ## 相关链接
## 关于分享协议 ## 关于分享协议

View File

@ -7,8 +7,8 @@ fonts_folder = "assets/fonts"
[window] [window]
style = "None" style = "None"
width = 1112 width = 1301
height = 793 height = 970
visible = true visible = true
gui_scale = 1 gui_scale = 1
caption = "Difficult Rocket v{DR_version}" caption = "Difficult Rocket v{DR_version}"

View File

@ -2,31 +2,8 @@
# DR game/DR rs 更新日志 # DR game/DR rs 更新日志
- 最新版本号 - 最新版本号
- DR game: 0.3.2.0 - DR game: 0.3.1.3
- DR rs: 0.2.19.0 - DR rs: 0.2.17.0
## 20230721 DR rs 0.2.19.0
### Add
- `PySR1Ship`
- `as_list`
## 20230721 DR game 0.3.2.0
### BUG Fix
- [#46](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/46)
- 渲染偏移 bug
## 20230721 DR rs 0.2.18.0
### Add
- 导出了 export
- `PySR1PartData`
- `PySaveStatus`
- `map_ptype_textures(part_type: str) -> str`
## 20230721 DR rs 0.2.17.0 ## 20230721 DR rs 0.2.17.0

View File

@ -9,81 +9,49 @@ from .lib import *
from typing import TYPE_CHECKING, Dict, Tuple, Optional, List from typing import TYPE_CHECKING, Dict, Tuple, Optional, List
if TYPE_CHECKING: if TYPE_CHECKING:
def test_call(py_obj) -> bool: def test_call(py_obj) -> bool:
""" 这里展示的代码实际上就是实际的等效实现 """ """ 这里展示的代码实际上就是实际的等效实现 """
py_obj.draw() py_obj.draw()
return True return True
def get_version_str() -> str: ...
def get_version_str() -> str: def part_list_read_test(file_name: Optional[str] = "./assets/builtin/PartList.xml") -> None: ...
"""
获取版本号 def read_ship_test(path: Optional[str] = "./assets/builtin/dock1.xml") -> None: ...
:return: 版本号
"""
def part_list_read_test(file_name: Optional[str] = "./assets/builtin/PartList.xml") -> None: class SR1PartType_rs:
""" """ 用于从 rust 中读取 SR1PartType
PartList 读取测试
:param file_name:
"""
def read_ship_test(path: Optional[str] = "./assets/builtin/dock1.xml") -> None:
"""
飞船存档读取测试
:param path:
:return:
"""
def map_ptype_textures(part_type: str) -> str:
"""
获取零件的贴图 (写死的)
:param part_type:
:return:
"""
class SR1PartType_rs: # NOQA
"""
用于从 rust 中读取 SR1PartType
不能从 Python 端创建 不能从 Python 端创建
自带一些可从 Python 端读取的属性 (实际上就是一个接口) 自带一些可从 Python 端读取的属性 (实际上就是一个接口)
""" """
@property @property
def name(self) -> str: def name(self) -> str:
""" 零件的名字 """ """ 零件的名字 """
@property @property
def description(self) -> str: def description(self) -> str:
""" 零件的描述 """ """ 零件的描述 """
@property @property
def mass(self) -> float: def mass(self) -> float:
""" 零件的质量 """ """ 零件的质量 """
@property @property
def width(self) -> int: def width(self) -> int:
""" 零件的宽度 """ """ 零件的宽度 """
@property @property
def height(self) -> int: def height(self) -> int:
""" 零件的高度 """ """ 零件的高度 """
@property @property
def friction(self) -> float: def friction(self) -> float:
""" 零件的摩擦系数 """ """ 零件的摩擦系数 """
@property @property
def hidden(self) -> bool: def hidden(self) -> bool:
""" 零件是否隐藏 """ """ 零件是否隐藏 """
class SR1PartList_rs: # NOQA class SR1PartList_rs:
""" 用于从 rust 中读取 SR1PartList """ """ 用于从 rust 中读取 SR1PartList """
def __init__(self, file_name: Optional[str] = "./assets/builtin/PartList.xml", def __init__(self, file_name: Optional[str] = "./assets/builtin/PartList.xml",
list_name: Optional[str] = 'NewPartList'): ... list_name: Optional[str] = 'NewPartList'): ...
@ -91,104 +59,63 @@ if TYPE_CHECKING:
def get_part_type(self, name: str) -> SR1PartType_rs: ... def get_part_type(self, name: str) -> SR1PartType_rs: ...
class SR1PartData_rs:
class SR1PartData_rs: # NOQA """ 用于从 rust 中读取 SR1PartData (其实好像也没啥用哈)
""" """
用于从 rust 中读取 SR1PartData (其实好像也没啥用哈)
"""
@property @property
def id(self) -> int: ... def id(self) -> int: ...
@property @property
def part_type_id(self) -> str: ... def part_type_id(self) -> str: ...
@property @property
def pos(self) -> Tuple[float, float]: ... def pos(self) -> Tuple[float, float]: ...
@property @property
def x(self) -> float: ... def x(self) -> float: ...
@property @property
def y(self) -> float: ... def y(self) -> float: ...
@property @property
def activate(self) -> bool: ... def activate(self) -> bool: ...
@property @property
def angle(self) -> float: ... def angle(self) -> float: ...
@property
def angle_r(self) -> float: ...
@property @property
def angle_v(self) -> float: ... def angle_v(self) -> float: ...
@property @property
def explode(self) -> bool: ... def explode(self) -> bool: ...
@property @property
def flip_x(self) -> bool: ... def flip_x(self) -> bool: ...
@property @property
def flip_y(self) -> bool: ... def flip_y(self) -> bool: ...
class SaveStatus_rs:
class SaveStatus_rs: # NOQA
def __init__(self, save_default: Optional[bool] = False) -> None: ... def __init__(self, save_default: Optional[bool] = False) -> None: ...
class SR1Ship_rs:
class SR1Ship_rs: # NOQA
""" 用于高效且省内存的读取 SR1Ship """ """ 用于高效且省内存的读取 SR1Ship """
def __init__(self, def __init__(self,
file_path: Optional[str] = './assets/builtin/dock1.xml', file_path: Optional[str] = './assets/builtin/dock1.xml',
part_list: Optional[SR1PartList_rs] = None, part_list: Optional[SR1PartList_rs] = None,
ship_name: Optional[str] = 'NewShip'): ... ship_name: Optional[str] = 'NewShip'): ...
@property @property
def name(self) -> str: ... def name(self) -> str: ...
@property @property
def description(self) -> str: ... def description(self) -> str: ...
@property @property
def lift_off(self) -> bool: ... def lift_off(self) -> bool: ...
@property @property
def touch_ground(self) -> bool: ... def touch_ground(self) -> bool: ...
@property @property
def mass(self) -> float: def img_pos(self) -> Tuple[int, int, int, int]: ...
""" 获取整搜船的质量 """ """ -x -y +x +y 左下右上 """
@property @property
def img_pos(self) -> Tuple[int, int, int, int]: def connection(self) -> List[Tuple[int, int, int, int]]: ...
""" -x -y +x +y 左下右上 """ """获取所有连接信息"""
def get_part_box(self, part_id: int) -> Optional[Tuple[Tuple[int, int], Tuple[int, int]]]: ...
@property
def connection(self) -> List[Tuple[int, int, int, int]]:
"""获取所有连接信息"""
def get_part_box(self, part_id: int) -> Optional[Tuple[Tuple[int, int], Tuple[int, int]]]:
"""获取所有零件的盒子"""
def as_list(self) -> List[Tuple[SR1PartType_rs, SR1PartData_rs]]:
"""用于返回一个包含所有已连接零件的列表"""
def as_dict(self) -> Dict[int, List[Tuple[SR1PartType_rs, SR1PartData_rs]]]: def as_dict(self) -> Dict[int, List[Tuple[SR1PartType_rs, SR1PartData_rs]]]:
"""用于返回一个包含所有已连接零件的字典""" """用于返回一个包含所有已连接零件的字典"""
def save(self, file_path: str, save_status: Optional[SaveStatus_rs] = None) -> None: ... def save(self, file_path: str, save_status: Optional[SaveStatus_rs] = None) -> None: ...
class Console_rs:
class Console_rs: # NOQA
def __init__(self) -> None: ... def __init__(self) -> None: ...
def start(self) -> None: ... def start(self) -> None: ...
def stop(self) -> bool: ... def stop(self) -> bool: ...
def get_command(self) -> Optional[str]: ... def get_command(self) -> Optional[str]: ...
def new_command(self) -> bool: ... def new_command(self) -> bool: ...

View File

@ -116,7 +116,7 @@ dependencies = [
[[package]] [[package]]
name = "difficult_rocket_rs" name = "difficult_rocket_rs"
version = "0.2.19" version = "0.2.17"
dependencies = [ dependencies = [
"pyo3", "pyo3",
"quick-xml", "quick-xml",

View File

@ -1,11 +1,8 @@
[package] [package]
name = "difficult_rocket_rs" name = "difficult_rocket_rs"
version = "0.2.19" version = "0.2.17"
edition = "2021" edition = "2021"
license-file = '../../LICENSE' license-file = '../../LICENSE'
authors = [
"shenjackyuanjie <3695888@qq.com>"
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -12,7 +12,7 @@ package_path = 'Difficult_Rocket_rs'
setup( setup(
name='Difficult_Rocket_rs', name='Difficult_Rocket_rs',
version="0.2.18.0", version="0.2.16.0",
author='shenjackyuanjie', author='shenjackyuanjie',
author_email='3695888@qq.com', author_email='3695888@qq.com',
rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs", rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs",

View File

@ -24,7 +24,7 @@ enum LoadState {
} }
#[pyfunction] #[pyfunction]
fn get_version_str() -> String { "0.2.19.0".to_string() } fn get_version_str() -> String { "0.2.17.0".to_string() }
#[pyfunction] #[pyfunction]
fn test_call(py_obj: &PyAny) -> PyResult<bool> { fn test_call(py_obj: &PyAny) -> PyResult<bool> {
@ -43,12 +43,9 @@ fn module_init(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sr1_data::part_list::read_part_list_py, m)?)?; m.add_function(wrap_pyfunction!(sr1_data::part_list::read_part_list_py, m)?)?;
m.add_function(wrap_pyfunction!(sr1_data::ship::py_raw_ship_from_file, m)?)?; m.add_function(wrap_pyfunction!(sr1_data::ship::py_raw_ship_from_file, m)?)?;
m.add_function(wrap_pyfunction!(python::data::load_and_save_test, m)?)?; m.add_function(wrap_pyfunction!(python::data::load_and_save_test, m)?)?;
m.add_function(wrap_pyfunction!(python::data::py_map_ptype_textures, m)?)?;
m.add_class::<python::data::PySR1Ship>()?; m.add_class::<python::data::PySR1Ship>()?;
m.add_class::<python::data::PySR1PartList>()?; m.add_class::<python::data::PySR1PartList>()?;
m.add_class::<python::data::PySR1PartType>()?; m.add_class::<python::data::PySR1PartType>()?;
m.add_class::<python::data::PySR1PartData>()?;
m.add_class::<python::data::PySaveStatus>()?;
m.add_class::<python::console::PyConsole>()?; m.add_class::<python::console::PyConsole>()?;
Ok(()) Ok(())
} }

View File

@ -13,15 +13,10 @@ pub mod data {
use crate::sr1_data::part_list::RawPartList; use crate::sr1_data::part_list::RawPartList;
use crate::types::math::{Point2D, Rotatable}; use crate::types::math::{Point2D, Rotatable};
use crate::types::sr1::{get_max_box, map_ptype_textures, SR1PartData, SR1PartListTrait}; use crate::types::sr1::{get_max_box, SR1PartData, SR1PartListTrait};
use crate::types::sr1::{IdType, SaveStatus}; use crate::types::sr1::{IdType, SaveStatus};
use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship}; use crate::types::sr1::{SR1PartList, SR1PartType, SR1Ship};
#[pyfunction]
#[pyo3(signature = (part_type))]
#[pyo3(name = "map_ptype_textures")]
pub fn py_map_ptype_textures(part_type: String) -> String { map_ptype_textures(part_type) }
#[pyclass] #[pyclass]
#[pyo3(name = "SaveStatus_rs")] #[pyo3(name = "SaveStatus_rs")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -142,9 +137,6 @@ pub mod data {
#[getter] #[getter]
fn get_angle(&self) -> f64 { self.data.angle } fn get_angle(&self) -> f64 { self.data.angle }
#[getter]
fn get_angle_r(&self) -> f64 { self.data.angle_degrees() }
#[getter] #[getter]
fn get_angle_v(&self) -> f64 { self.data.angle_v } fn get_angle_v(&self) -> f64 { self.data.angle_v }
@ -229,20 +221,8 @@ pub mod data {
connections connections
} }
fn as_list(&self) -> Vec<(PySR1PartType, PySR1PartData)> { fn as_dict(&self) -> HashMap<i64, Vec<(PySR1PartType, PySR1PartData)>> {
let mut parts: Vec<(PySR1PartType, PySR1PartData)> = Vec::new(); let mut parts: HashMap<i64, Vec<(PySR1PartType, PySR1PartData)>> = HashMap::new();
for part_data in self.ship.parts.iter() {
if let Some(part_type) = self.part_list.get_part_type(&part_data.part_type_id) {
let part_type = PySR1PartType::new(part_type.clone());
let py_part_data = PySR1PartData::new(part_data.clone());
parts.push((part_type, py_part_data));
}
}
parts
}
fn as_dict(&self) -> HashMap<IdType, Vec<(PySR1PartType, PySR1PartData)>> {
let mut parts: HashMap<IdType, Vec<(PySR1PartType, PySR1PartData)>> = HashMap::new();
for part_data in self.ship.parts.iter() { for part_data in self.ship.parts.iter() {
if let Some(part_type) = self.part_list.get_part_type(&part_data.part_type_id) { if let Some(part_type) = self.part_list.get_part_type(&part_data.part_type_id) {
let part_type = PySR1PartType::new(part_type.clone()); let part_type = PySR1PartType::new(part_type.clone());

View File

@ -24,29 +24,6 @@ pub mod sr1 {
pub type IdType = i64; pub type IdType = i64;
pub type ConnectionsType = Vec<(Vec<SR1PartData>, Option<Vec<Connection>>)>; pub type ConnectionsType = Vec<(Vec<SR1PartData>, Option<Vec<Connection>>)>;
#[inline]
pub fn radians_map_to_degrees(angle: f64) -> f64 {
// match angle {
// 0.0 => 0.,
// 1.570796 => 270.,
// 3.141593 => 180.,
// 4.712389 => 90.,
// _ => {
// angle.to_degrees()
// }
// }
#[allow(clippy::approx_constant)]
if angle == 1.570796 {
270.
} else if angle == 3.141593 {
180.
} else if angle == 4.712389 {
90.
} else {
angle.to_degrees()
}
}
#[allow(unused)] #[allow(unused)]
#[inline] #[inline]
pub fn map_ptype_textures(ptype: String) -> String { pub fn map_ptype_textures(ptype: String) -> String {
@ -529,8 +506,6 @@ pub mod sr1 {
} }
pos_box pos_box
} }
pub fn angle_degrees(&self) -> f64 { radians_map_to_degrees(self.angle) }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -16,7 +16,7 @@ from Difficult_Rocket.api.mod import ModInfo
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
from Difficult_Rocket.api.types import Options, Version from Difficult_Rocket.api.types import Options, Version
DR_rust_version = Version("0.2.19.0") # DR_mod 的 Rust 编写部分的兼容版本 DR_rust_version = Version("0.2.17.0") # DR_mod 的 Rust 编写部分的兼容版本
logger = logging.getLogger('client.dr_game') logger = logging.getLogger('client.dr_game')
@ -53,7 +53,7 @@ DR_mod_runtime = _DR_mod_runtime()
class DR_mod(ModInfo): # NOQA class DR_mod(ModInfo): # NOQA
mod_id = "difficult_rocket_mod" mod_id = "difficult_rocket_mod"
name = "Difficult Rocket mod" name = "Difficult Rocket mod"
version = Version("0.3.2.0") version = Version("0.3.1.2")
writer = "shenjackyuanjie" writer = "shenjackyuanjie"
link = "shenjack.top" link = "shenjack.top"
@ -81,7 +81,7 @@ class DR_mod(ModInfo): # NOQA
else: else:
self.config.flush_option() self.config.flush_option()
logger.info("on_load") logger.info("on_load")
logger.info(f"\n{self.as_markdown()}") logger.info(self.as_markdown())
return True return True
def on_client_start(self, game: Game, client: ClientWindow): def on_client_start(self, game: Game, client: ClientWindow):

View File

@ -40,16 +40,38 @@ if TYPE_CHECKING:
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
from .Difficult_Rocket_rs import (SR1PartList_rs, from .Difficult_Rocket_rs import (SR1PartList_rs,
SR1Ship_rs, SR1Ship_rs)
SR1PartData_rs,
SR1PartType_rs,
map_ptype_textures)
logger = logging.getLogger('client.dr_game_sr1_ship') logger = logging.getLogger('client.dr_game_sr1_ship')
logger.level = logging.DEBUG logger.level = logging.DEBUG
sr_tr = Tr(lang_path=Path(__file__).parent / 'lang') sr_tr = Tr(lang_path=Path(__file__).parent / 'lang')
def get_sr1_part(part_xml: Element) -> Optional[SR1PartData]:
if part_xml.tag != 'Part':
return None
# print(f"tag: {part.tag} attrib: {part.attrib}")
part_id = int(part_xml.attrib.get('id'))
part_type = part_xml.attrib.get('partType')
part_x = float(part_xml.attrib.get('x'))
part_y = float(part_xml.attrib.get('y'))
part_activate = xml_bool(part_xml.attrib.get('activated'))
part_angle = float(part_xml.attrib.get('angle'))
part_angle_v = float(part_xml.attrib.get('angleV'))
part_editor_angle = int(part_xml.attrib.get('editorAngle'))
part_flip_x = xml_bool(part_xml.attrib.get('flippedX'))
part_flip_y = xml_bool(part_xml.attrib.get('flippedY'))
part_explode = xml_bool(part_xml.attrib.get('exploded'))
if part_type not in SR1PartTexture.part_type_sprite:
part_textures = None
else:
part_textures = SR1PartTexture.get_textures_from_type(part_type)
return SR1PartData(x=part_x, y=part_y, id=part_id, p_type=part_type,
active=part_activate, angle=part_angle, angle_v=part_angle_v,
editor_angle=part_editor_angle, flip_x=part_flip_x,
flip_y=part_flip_y, explode=part_explode, textures=part_textures)
class SR1ShipRender_Option(Options): # NOQA class SR1ShipRender_Option(Options): # NOQA
# debug option # debug option
debug_d_pos: bool = False debug_d_pos: bool = False
@ -75,8 +97,6 @@ class SR1ShipRender(BaseScreen):
self.render_option = SR1ShipRender_Option() self.render_option = SR1ShipRender_Option()
self.dx = 0 self.dx = 0
self.dy = 0 self.dy = 0
self.width = main_window.width
self.height = main_window.height
self.main_batch = Batch() self.main_batch = Batch()
self.part_group = Group(10, parent=main_window.main_group) self.part_group = Group(10, parent=main_window.main_group)
@ -92,29 +112,32 @@ class SR1ShipRender(BaseScreen):
self.render_d_label = Label('debug label NODATA', font_name=Fonts.微软等宽无线, self.render_d_label = Label('debug label NODATA', font_name=Fonts.微软等宽无线,
x=main_window.width / 2, y=main_window.height / 2) x=main_window.width / 2, y=main_window.height / 2)
self.render_d_label.visible = self.render_option.debug_d_pos self.render_d_label.visible = self.render_option.debug_d_pos
self.camera = CenterCamera(main_window, min_zoom=(1 / 2) ** 10, max_zoom=10)
# Optional data # Optional data
self.textures: SR1Textures = SR1Textures()
self.gen_draw: Optional[Generator] = None self.gen_draw: Optional[Generator] = None
self.textures: Union[SR1Textures, None] = None
self.xml_name: Optional[str] = None # 准备移除, 更换为基于 rust 的 xml 解析
self.xml_doc: Optional[ElementTree] = None # 准备移除, 更换为基于 rust 的 xml 解析
self.xml_root: Optional[Element] = None # 准备移除, 更换为基于 rust 的 xml 解析
self.rust_ship: Optional[SR1Ship_rs] = None self.rust_ship: Optional[SR1Ship_rs] = None
self.ship_name: Optional[str] = None
# List/Dict data # List/Dict data
self.parts_sprite: Dict[int, List[Sprite]] = {} self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {}
self.part_box_dict: Dict[int, Rectangle] = {} self.part_box_dict: Dict[int, Rectangle] = {}
self.part_line_box: Dict[int, List[Line]] = {} self.part_line_box: Dict[int, List[Line]] = {}
self.part_line_list: List[Line] = [] self.part_line_list: List[Line] = []
load_end_time = time.time_ns()
logger.info(sr_tr().mod.info.setup.use_time().format((load_end_time - load_start_time) / 1000000000))
self.camera = CenterCamera(main_window, min_zoom=(1 / 2) ** 10, max_zoom=10)
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
self.rust_parts = None self.rust_parts = None
self.part_list_rs = SR1PartList_rs('assets/builtin/PartList.xml', 'builtin_part_list') self.part_list_rs = SR1PartList_rs('assets/builtin/PartList.xml', 'builtin_part_list')
self.load_xml('assets/builtin/dock1.xml') self.load_xml('assets/builtin/dock1.xml')
load_end_time = time.time_ns()
logger.info(sr_tr().mod.info.setup.use_time().format((load_end_time - load_start_time) / 1000000000))
def load_xml(self, file_path: str) -> bool: def load_xml(self, file_path: str) -> bool:
""" """
加载 xml 文件 加载 xml 文件
@ -124,7 +147,10 @@ class SR1ShipRender(BaseScreen):
try: try:
start_time = time.time_ns() start_time = time.time_ns()
logger.info(sr_tr().sr1.ship.xml.loading().format(file_path)) logger.info(sr_tr().sr1.ship.xml.loading().format(file_path))
self.ship_name = file_path.split('/')[-1].split('.')[0] cache_doc = parse(file_path)
self.xml_doc = cache_doc
self.xml_root = self.xml_doc.getroot()
self.xml_name = file_path
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
self.rust_ship = SR1Ship_rs(file_path, self.part_list_rs, 'a_new_ship') self.rust_ship = SR1Ship_rs(file_path, self.part_list_rs, 'a_new_ship')
logger.info(sr_tr().sr1.ship.xml.load_done()) logger.info(sr_tr().sr1.ship.xml.load_done())
@ -135,34 +161,44 @@ class SR1ShipRender(BaseScreen):
print(e) print(e)
return False return False
def gen_sprite(self, each_count: int = 100) -> Generator: def load_textures(self):
"""
初始化纹理加载
:return:
"""
self.textures = SR1Textures()
def gen_sprite(self, part_datas: Dict[int, SR1PartData], each_count: int = 100) -> Generator:
""" """
生成 sprite 生成 sprite
通过生成器减少一次性渲染的压力 通过生成器减少一次性渲染的压力
:param part_datas: 所有的部件数据
:param each_count: 每次生成的数量 (默认 100) (过大会导致卡顿) :param each_count: 每次生成的数量 (默认 100) (过大会导致卡顿)
:return: 生成器 :return: 生成器
""" """
count = 0 count = 0
self.drawing = True self.drawing = True
# rust 渲染 for part_id, part in part_datas.items():
if DR_mod_runtime.use_DR_rust: # 下面就是调用 pyglet 去渲染的部分
cache = self.rust_ship.as_dict() # render_scale = DR_status.gui_scale # 这个是 DR 的缩放比例 可以调节的
for p_id, parts in cache.items(): # 在不缩放的情况下XML的1个单位长度对应60个像素
p_id: int render_x = part.x * 60
parts: List[Tuple[SR1PartType_rs, SR1PartData_rs]] render_y = part.y * 60
part_group = Group(2, parent=self.part_group) cache_sprite = Sprite(img=self.textures.get_texture(part.textures),
batch = [] x=render_x, y=render_y, z=random.random(),
for p_type, p_data in parts: batch=self.main_batch, group=self.part_group)
part_sprite = Sprite(img=self.textures.get_texture(map_ptype_textures(p_data.part_type_id)), # 你得帮我换算一下 XML 里的 x y 和这里的屏幕像素的关系OK
x=p_data.x * 60, y=p_data.y * 60, z=random.random(), # 旋转啥的不是大问题, 我找你要那个渲染代码就是要 x y 的换算逻辑
batch=self.main_batch, group=part_group) cache_sprite.rotation = SR1Rotation.get_rotation(part.angle)
part_sprite.rotation = p_data.angle_r if part.flip_x:
part_sprite.scale_x = -1 if p_data.flip_x else 1 cache_sprite.scale_x = -1
part_sprite.scale_y = -1 if p_data.flip_y else 1 if part.flip_y:
cache_sprite.scale_y = -1
self.parts_sprite[part.id] = cache_sprite
batch.append(part_sprite) if DR_mod_runtime.use_DR_rust:
line_box_group = Group(6, parent=self.part_group) line_box_group = Group(6, parent=self.part_group)
part_debug_box = self.rust_ship.get_part_box(p_id) part_debug_box = self.rust_ship.get_part_box(part.id)
if part_debug_box: if part_debug_box:
# 线框 # 线框
part_line_box = [] part_line_box = []
@ -181,17 +217,17 @@ class SR1ShipRender(BaseScreen):
part_line_box.append(Line(x=part_debug_box[1][0] * 30, y=part_debug_box[0][1] * 30, part_line_box.append(Line(x=part_debug_box[1][0] * 30, y=part_debug_box[0][1] * 30,
x2=part_debug_box[0][0] * 30, y2=part_debug_box[0][1] * 30, x2=part_debug_box[0][0] * 30, y2=part_debug_box[0][1] * 30,
batch=self.main_batch, width=width, color=color, group=line_box_group)) batch=self.main_batch, width=width, color=color, group=line_box_group))
self.part_line_box[p_id] = part_line_box self.part_line_box[part.id] = part_line_box
self.parts_sprite[p_id] = batch count += 1
count += 1 if count >= each_count:
if count >= each_count: count = 0
count = 0 yield count
yield if DR_mod_runtime.use_DR_rust:
connect_line_group = Group(7, parent=self.part_group) connect_line_group = Group(7, parent=self.part_group)
for connect in self.rust_ship.connection: for connect in self.rust_ship.connection:
# 连接线 # 连接线
parent_part_data = cache[connect[2]][0][1] parent_part_data = self.part_data[connect[2]]
child_part_data = cache[connect[3]][0][1] child_part_data = self.part_data[connect[3]]
color = (random.randrange(100, 255), random.randrange(0, 255), random.randrange(0, 255), 255) color = (random.randrange(100, 255), random.randrange(0, 255), random.randrange(0, 255), 255)
self.part_line_list.append(Line(x=parent_part_data.x * 60, y=parent_part_data.y * 60, self.part_line_list.append(Line(x=parent_part_data.x * 60, y=parent_part_data.y * 60,
x2=child_part_data.x * 60, y2=child_part_data.y * 60, x2=child_part_data.x * 60, y2=child_part_data.y * 60,
@ -201,31 +237,6 @@ class SR1ShipRender(BaseScreen):
if count >= each_count * 3: if count >= each_count * 3:
count = 0 count = 0
yield count yield count
# python 渲染
# for part_id, part in part_datas.items():
# # 下面就是调用 pyglet 去渲染的部分
# # render_scale = DR_status.gui_scale # 这个是 DR 的缩放比例 可以调节的
# # 在不缩放的情况下XML的1个单位长度对应60个像素
# # render_x = part.x * 60
# # render_y = part.y * 60
# # cache_sprite = Sprite(img=self.textures.get_texture(part.textures),
# # x=render_x, y=render_y, z=random.random(),
# # batch=self.main_batch, group=self.part_group)
# # # 你得帮我换算一下 XML 里的 x y 和这里的屏幕像素的关系
# # # 旋转啥的不是大问题, 我找你要那个渲染代码就是要 x y 的换算逻辑
# # cache_sprite.rotation = SR1Rotation.get_rotation(part.angle)
# # if part.flip_x:
# # cache_sprite.scale_x = -1
# # if part.flip_y:
# # cache_sprite.scale_y = -1
# # self.parts_sprite[part.id] = cache_sprite
#
# if DR_mod_runtime.use_DR_rust:
# count += 1
# if count >= each_count:
# count = 0
# yield count
self.drawing = False self.drawing = False
raise GeneratorExit raise GeneratorExit
@ -233,28 +244,40 @@ class SR1ShipRender(BaseScreen):
""" """
渲染船 渲染船
""" """
logger.info(sr_tr().sr1.ship.ship.load().format(self.ship_name)) if self.textures is None:
self.load_textures()
logger.info(sr_tr().sr1.ship.ship.load().format(self.xml_name))
start_time = time.perf_counter_ns() start_time = time.perf_counter_ns()
self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {} self.parts_sprite: Dict[int, Sprite] = {}
self.part_line_box = {} self.part_line_box = {}
self.part_line_list = [] self.part_line_list = []
# self.camera.zoom = 1.0 self.camera.zoom = 1.0
# self.camera.dx = 0 self.camera.dx = 0
# self.camera.dy = 0 self.camera.dy = 0
parts = self.xml_root.find('Parts')
for part_xml in parts:
if part_xml.tag != 'Part':
continue # 如果不是部件,则跳过
part = get_sr1_part(part_xml)
if part.id in self.part_data:
print(f'hey! warning! id{part.id}')
self.part_data[part.id] = part
# 调用生成器 减少卡顿 # 调用生成器 减少卡顿
try: try:
self.gen_draw = self.gen_sprite() self.gen_draw = self.gen_sprite(self.part_data)
next(self.gen_draw) next(self.gen_draw)
except (GeneratorExit, StopIteration): except GeneratorExit:
self.drawing = False self.drawing = False
self.need_draw = False self.need_draw = False
full_mass = 0 full_mass = 0
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
full_mass = self.rust_ship.mass for part in self.part_data:
full_mass += self.part_list_rs.get_part_type(self.part_data[part].p_type).mass * 500
logger.info(sr_tr().sr1.ship.ship.load_time().format( logger.info(sr_tr().sr1.ship.ship.load_time().format(
(time.perf_counter_ns() - start_time) / 1000000000)) (time.perf_counter_ns() - start_time) / 1000000000))
logger.info(sr_tr().sr1.ship.ship.info().format( logger.info(sr_tr().sr1.ship.ship.info().format(
len(self.rust_ship.as_list()), f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else sr_tr().game.require_DR_rs())) len(self.part_data), f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else sr_tr().game.require_DR_rs()))
self.rendered = True self.rendered = True
def draw_batch(self, window: "ClientWindow"): def draw_batch(self, window: "ClientWindow"):
@ -276,7 +299,7 @@ class SR1ShipRender(BaseScreen):
if self.drawing: if self.drawing:
try: try:
next(self.gen_draw) next(self.gen_draw)
except (GeneratorExit, StopIteration): except GeneratorExit:
self.drawing = False self.drawing = False
self.logger.info(sr_tr().sr1.ship.ship.render.done()) self.logger.info(sr_tr().sr1.ship.ship.render.done())

View File

@ -33,8 +33,10 @@ class SR1PartData:
class SR1Textures(Options): class SR1Textures(Options):
""" 存储 sr1 的材质 img """ """ 存储 sr1 的材质 img """
def load_file(self, **kwargs): def __init__(self, **kwargs):
for image_name in self.flush_option(): super().__init__(**kwargs)
self.flush_option()
for image_name in self.cached_options:
img = load(f'assets/textures/parts/{image_name}.png') img = load(f'assets/textures/parts/{image_name}.png')
img.anchor_x = img.width // 2 img.anchor_x = img.width // 2
img.anchor_y = img.height // 2 img.anchor_y = img.height // 2
@ -47,14 +49,8 @@ class SR1Textures(Options):
:param name: :param name:
:return: :return:
""" """
if name in self.cached_options: assert name in self.cached_options
return self.cached_options.get(name) return self.cached_options.get(name)
else:
img = load(f'assets/textures/parts/{name}.png')
img.anchor_x = img.width // 2
img.anchor_y = img.height // 2
setattr(self, name, img)
return img
Battery: AbstractImage = None Battery: AbstractImage = None
Beam: AbstractImage = None Beam: AbstractImage = None
@ -138,11 +134,6 @@ class SR1Rotation(Options):
@classmethod @classmethod
def get_rotation(cls, radian: float) -> float: def get_rotation(cls, radian: float) -> float:
"""
实际上就是将弧度转换为角度 (同时自带一个映射表)
:param radian:
:return:
"""
if radian in cls.radian_angle_map: if radian in cls.radian_angle_map:
return cls.radian_angle_map[radian] return cls.radian_angle_map[radian]
else: else: