Merge branch 'dr_game/gen_img'
This commit is contained in:
commit
640cc27e34
@ -264,7 +264,7 @@ class ClientWindow(Window):
|
||||
def start_game(self) -> None:
|
||||
self.set_icon(pyglet.image.load('assets/textures/icon.png'))
|
||||
try:
|
||||
pyglet.app.event_loop.run(1 / self.main_config['runtime']['fps'])
|
||||
pyglet.app.run(1 / self.main_config['runtime']['fps'])
|
||||
except KeyboardInterrupt:
|
||||
self.logger.warning("==========client stop. KeyboardInterrupt info==========")
|
||||
traceback.print_exc()
|
||||
|
@ -1,3 +1,5 @@
|
||||
<div style="text-align: center;">
|
||||
|
||||
# Difficult Rocket
|
||||
|
||||
中文 | [English](./docs/README-en.md)
|
||||
@ -119,6 +121,8 @@ setuptools-rust >= 1.6.0
|
||||
- [@Billchyi](https://github.com/Billchyi) : 文档矫正
|
||||
- [@MSDNicrosoft](https://github.com/MSDNicrosoft) : 优化代码
|
||||
|
||||
</div>
|
||||
|
||||
## 相关链接
|
||||
|
||||
## 关于分享协议
|
||||
|
@ -7,8 +7,8 @@ fonts_folder = "assets/fonts"
|
||||
|
||||
[window]
|
||||
style = "None"
|
||||
width = 1301
|
||||
height = 970
|
||||
width = 1112
|
||||
height = 793
|
||||
visible = true
|
||||
gui_scale = 1
|
||||
caption = "Difficult Rocket v{DR_version}"
|
||||
|
@ -2,8 +2,67 @@
|
||||
# DR game/DR rs 更新日志
|
||||
|
||||
- 最新版本号
|
||||
- DR game: 0.3.1.3
|
||||
- DR rs: 0.2.17.0
|
||||
- DR game: 0.3.2.0
|
||||
- DR rs: 0.2.20.2
|
||||
|
||||
## 20230724 DR rs 0.2.20.2
|
||||
|
||||
### Fix
|
||||
|
||||
- [#49](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/49)
|
||||
- missing field `touchingGround`
|
||||
- SR1 says: `touchingGround` field is NOT Required
|
||||
- make them happy
|
||||
- SR1 说: `touchingGround` 字段也是可选的
|
||||
- 让他们开心
|
||||
- 我就看看我能发多少个 issue
|
||||
|
||||
## 20230724 DR rs 0.2.20.1
|
||||
|
||||
### Fix
|
||||
|
||||
- [#48](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/48)
|
||||
- `missing field version`
|
||||
- SR1 says: `version` field is NOT Required
|
||||
- make them happy
|
||||
- SR1 说: `version` 字段也是可选的
|
||||
- 让他们开心
|
||||
- 我谢谢您啊 Jundroo
|
||||
- 我就看看我能发多少个 issue
|
||||
|
||||
## 20230724 DR rs 0.2.20.0
|
||||
|
||||
### Fix
|
||||
|
||||
- [#47](https://github.com/shenjackyuanjie/Difficult-Rocket/issues/47)
|
||||
- `editorAngle field is Option<i32>`
|
||||
- SR1 says: `editorAngle` field is Optional
|
||||
- make them happy
|
||||
- SR1 说: `editorAngle` 字段是可选的
|
||||
- 让他们开心
|
||||
|
||||
## 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
|
||||
|
||||
|
@ -9,49 +9,81 @@ from .lib import *
|
||||
from typing import TYPE_CHECKING, Dict, Tuple, Optional, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
def test_call(py_obj) -> bool:
|
||||
""" 这里展示的代码实际上就是实际的等效实现 """
|
||||
py_obj.draw()
|
||||
return True
|
||||
|
||||
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: ...
|
||||
def get_version_str() -> str:
|
||||
"""
|
||||
获取版本号
|
||||
:return: 版本号
|
||||
"""
|
||||
|
||||
|
||||
class SR1PartType_rs:
|
||||
""" 用于从 rust 中读取 SR1PartType
|
||||
def part_list_read_test(file_name: Optional[str] = "./assets/builtin/PartList.xml") -> None:
|
||||
"""
|
||||
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 端读取的属性 (实际上就是一个接口)
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""" 零件的名字 """
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
""" 零件的描述 """
|
||||
|
||||
@property
|
||||
def mass(self) -> float:
|
||||
""" 零件的质量 """
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
""" 零件的宽度 """
|
||||
|
||||
@property
|
||||
def height(self) -> int:
|
||||
""" 零件的高度 """
|
||||
|
||||
@property
|
||||
def friction(self) -> float:
|
||||
""" 零件的摩擦系数 """
|
||||
|
||||
@property
|
||||
def hidden(self) -> bool:
|
||||
""" 零件是否隐藏 """
|
||||
|
||||
|
||||
class SR1PartList_rs:
|
||||
class SR1PartList_rs: # NOQA
|
||||
""" 用于从 rust 中读取 SR1PartList """
|
||||
|
||||
def __init__(self, file_name: Optional[str] = "./assets/builtin/PartList.xml",
|
||||
list_name: Optional[str] = 'NewPartList'): ...
|
||||
|
||||
@ -59,63 +91,111 @@ if TYPE_CHECKING:
|
||||
|
||||
def get_part_type(self, name: str) -> SR1PartType_rs: ...
|
||||
|
||||
class SR1PartData_rs:
|
||||
""" 用于从 rust 中读取 SR1PartData (其实好像也没啥用哈)
|
||||
|
||||
class SR1PartData_rs: # NOQA
|
||||
"""
|
||||
用于从 rust 中读取 SR1PartData (其实好像也没啥用哈)
|
||||
"""
|
||||
|
||||
@property
|
||||
def id(self) -> int: ...
|
||||
|
||||
@property
|
||||
def part_type_id(self) -> str: ...
|
||||
|
||||
@property
|
||||
def pos(self) -> Tuple[float, float]: ...
|
||||
|
||||
@property
|
||||
def x(self) -> float: ...
|
||||
|
||||
@property
|
||||
def y(self) -> float: ...
|
||||
|
||||
@property
|
||||
def activate(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def angle(self) -> float: ...
|
||||
|
||||
@property
|
||||
def angle_r(self) -> float: ...
|
||||
|
||||
@property
|
||||
def angle_v(self) -> float: ...
|
||||
|
||||
@property
|
||||
def explode(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def flip_x(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def flip_y(self) -> bool: ...
|
||||
|
||||
class SaveStatus_rs:
|
||||
|
||||
class SaveStatus_rs: # NOQA
|
||||
def __init__(self, save_default: Optional[bool] = False) -> None: ...
|
||||
|
||||
class SR1Ship_rs:
|
||||
|
||||
class SR1Ship_rs: # NOQA
|
||||
""" 用于高效且省内存的读取 SR1Ship """
|
||||
|
||||
def __init__(self,
|
||||
file_path: Optional[str] = './assets/builtin/dock1.xml',
|
||||
part_list: Optional[SR1PartList_rs] = None,
|
||||
ship_name: Optional[str] = 'NewShip'): ...
|
||||
ship_name: Optional[str] = 'NewShip'):
|
||||
"""
|
||||
读取 SR1Ship
|
||||
:raise ValueError: 读取失败
|
||||
:param file_path:
|
||||
:param part_list:
|
||||
:param ship_name:
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
|
||||
@property
|
||||
def description(self) -> str: ...
|
||||
|
||||
@property
|
||||
def lift_off(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def touch_ground(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def img_pos(self) -> Tuple[int, int, int, int]: ...
|
||||
""" -x -y +x +y 左下右上 """
|
||||
def mass(self) -> float:
|
||||
""" 获取整搜船的质量 """
|
||||
|
||||
@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 img_pos(self) -> Tuple[int, int, int, int]:
|
||||
""" -x -y +x +y 左下右上 """
|
||||
|
||||
@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 save(self, file_path: str, save_status: Optional[SaveStatus_rs] = None) -> None: ...
|
||||
|
||||
class Console_rs:
|
||||
|
||||
class Console_rs: # NOQA
|
||||
def __init__(self) -> None: ...
|
||||
|
||||
def start(self) -> None: ...
|
||||
|
||||
def stop(self) -> bool: ...
|
||||
|
||||
def get_command(self) -> Optional[str]: ...
|
||||
|
||||
def new_command(self) -> bool: ...
|
||||
|
2
mods/dr_game/Difficult_Rocket_rs/src/Cargo.lock
generated
2
mods/dr_game/Difficult_Rocket_rs/src/Cargo.lock
generated
@ -116,7 +116,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "difficult_rocket_rs"
|
||||
version = "0.2.17"
|
||||
version = "0.2.20"
|
||||
dependencies = [
|
||||
"pyo3",
|
||||
"quick-xml",
|
||||
|
@ -1,8 +1,11 @@
|
||||
[package]
|
||||
name = "difficult_rocket_rs"
|
||||
version = "0.2.17"
|
||||
version = "0.2.20"
|
||||
edition = "2021"
|
||||
license-file = '../../LICENSE'
|
||||
authors = [
|
||||
"shenjackyuanjie <3695888@qq.com>"
|
||||
]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -12,7 +12,7 @@ package_path = 'Difficult_Rocket_rs'
|
||||
|
||||
setup(
|
||||
name='Difficult_Rocket_rs',
|
||||
version="0.2.16.0",
|
||||
version="0.2.18.0",
|
||||
author='shenjackyuanjie',
|
||||
author_email='3695888@qq.com',
|
||||
rust_extensions=[RustExtension(target="Difficult_Rocket_rs.Difficult_Rocket_rs",
|
||||
|
@ -24,7 +24,7 @@ enum LoadState {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_version_str() -> String { "0.2.17.0".to_string() }
|
||||
fn get_version_str() -> String { "0.2.20.2".to_string() }
|
||||
|
||||
#[pyfunction]
|
||||
fn test_call(py_obj: &PyAny) -> PyResult<bool> {
|
||||
@ -43,9 +43,12 @@ 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::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::py_map_ptype_textures, m)?)?;
|
||||
m.add_class::<python::data::PySR1Ship>()?;
|
||||
m.add_class::<python::data::PySR1PartList>()?;
|
||||
m.add_class::<python::data::PySR1PartType>()?;
|
||||
m.add_class::<python::data::PySR1PartData>()?;
|
||||
m.add_class::<python::data::PySaveStatus>()?;
|
||||
m.add_class::<python::console::PyConsole>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -9,14 +9,20 @@
|
||||
pub mod data {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pyo3::exceptions::PyValueError;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use crate::sr1_data::part_list::RawPartList;
|
||||
use crate::types::math::{Point2D, Rotatable};
|
||||
use crate::types::sr1::{get_max_box, SR1PartData, SR1PartListTrait};
|
||||
use crate::types::sr1::{get_max_box, map_ptype_textures, SR1PartData, SR1PartListTrait};
|
||||
use crate::types::sr1::{IdType, SaveStatus};
|
||||
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]
|
||||
#[pyo3(name = "SaveStatus_rs")]
|
||||
#[derive(Clone, Debug)]
|
||||
@ -137,6 +143,9 @@ pub mod data {
|
||||
#[getter]
|
||||
fn get_angle(&self) -> f64 { self.data.angle }
|
||||
|
||||
#[getter]
|
||||
fn get_angle_r(&self) -> f64 { self.data.angle_degrees() }
|
||||
|
||||
#[getter]
|
||||
fn get_angle_v(&self) -> f64 { self.data.angle_v }
|
||||
|
||||
@ -162,14 +171,19 @@ pub mod data {
|
||||
impl PySR1Ship {
|
||||
#[new]
|
||||
#[pyo3(text_signature = "(file_path = './assets/builtin/dock1.xml', part_list = None, ship_name = 'NewShip')")]
|
||||
fn new(file_path: String, part_list: Option<PySR1PartList>, ship_name: Option<String>) -> Self {
|
||||
let mut ship = SR1Ship::from_file(file_path, Some(ship_name.unwrap_or("new ship".to_string()))).unwrap();
|
||||
let part_list = match part_list {
|
||||
Some(part_list) => part_list.data,
|
||||
None => SR1PartList::from_file("./assets/builtin/PartList.xml".to_string()).unwrap(),
|
||||
};
|
||||
ship.parse_part_list_to_part(&part_list);
|
||||
Self { ship, part_list }
|
||||
fn new(file_path: String, part_list: Option<PySR1PartList>, ship_name: Option<String>) -> PyResult<Self> {
|
||||
let ship = SR1Ship::from_file(file_path.clone(), Some(ship_name.unwrap_or("new ship".to_string())));
|
||||
match ship {
|
||||
Some(mut ship) => {
|
||||
let part_list = match part_list {
|
||||
Some(part_list) => part_list.data,
|
||||
None => SR1PartList::from_file("./assets/builtin/PartList.xml".to_string()).unwrap(),
|
||||
};
|
||||
ship.parse_part_list_to_part(&part_list);
|
||||
Ok(Self { ship, part_list })
|
||||
}
|
||||
None => Err(PyValueError::new_err(format!("Parse ship failed! {}", file_path))),
|
||||
}
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@ -221,8 +235,20 @@ pub mod data {
|
||||
connections
|
||||
}
|
||||
|
||||
fn as_dict(&self) -> HashMap<i64, Vec<(PySR1PartType, PySR1PartData)>> {
|
||||
let mut parts: HashMap<i64, Vec<(PySR1PartType, PySR1PartData)>> = HashMap::new();
|
||||
fn as_list(&self) -> Vec<(PySR1PartType, PySR1PartData)> {
|
||||
let mut parts: Vec<(PySR1PartType, PySR1PartData)> = Vec::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() {
|
||||
if let Some(part_type) = self.part_list.get_part_type(&part_data.part_type_id) {
|
||||
let part_type = PySR1PartType::new(part_type.clone());
|
||||
|
@ -377,7 +377,7 @@ pub mod ship {
|
||||
|
||||
use super::part_list::SR1PartTypeEnum;
|
||||
|
||||
use crate::types::sr1::{i8_to_bool, SR1PartDataTrait, SR1PartTypeAttr, SR1ShipTrait};
|
||||
use crate::types::sr1::{i8_to_bool, option_i8_to_option_bool, SR1PartDataTrait, SR1PartTypeAttr, SR1ShipTrait};
|
||||
use crate::types::sr1::{IdType, SR1PartData, SR1PartDataAttr, SR1Ship};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@ -387,11 +387,15 @@ pub mod ship {
|
||||
pub parts: Parts,
|
||||
#[serde(rename = "Connections")]
|
||||
pub connects: Connections,
|
||||
pub version: i32,
|
||||
pub version: Option<i32>, // Option for https://github.com/shenjackyuanjie/Difficult-Rocket/issues/48
|
||||
// SR1 says version is also optional, let them happy
|
||||
// it's always 1
|
||||
#[serde(rename = "liftedOff")]
|
||||
pub lift_off: i8,
|
||||
#[serde(rename = "touchingGround")]
|
||||
pub touch_ground: i8,
|
||||
pub touch_ground: Option<i8>, // Option for https://github.com/shenjackyuanjie/Difficult-Rocket/issues/49
|
||||
// SR1 says it's optional, let them happy
|
||||
// NOT always 0
|
||||
#[serde(rename = "DisconnectedParts")]
|
||||
pub disconnected: Option<DisconnectedParts>,
|
||||
}
|
||||
@ -422,7 +426,8 @@ pub mod ship {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
#[serde(rename = "editorAngle")]
|
||||
pub editor_angle: i32,
|
||||
pub editor_angle: Option<i32>, // Option for https://github.com/shenjackyuanjie/Difficult-Rocket/issues/47
|
||||
// SR1 says it's optional, let them happy
|
||||
pub angle: f64,
|
||||
#[serde(rename = "angleV")]
|
||||
pub angle_v: f64,
|
||||
@ -515,12 +520,6 @@ pub mod ship {
|
||||
pub child_part: IdType,
|
||||
}
|
||||
|
||||
impl Part {
|
||||
/// 根据 Part 的原始数据猜测 Part 的类型
|
||||
/// jundroo 我日你先人
|
||||
fn guess_part_type(&self) -> SR1PartTypeEnum { todo!() }
|
||||
}
|
||||
|
||||
impl SR1PartDataTrait for Part {
|
||||
fn to_sr_part_data(&self) -> SR1PartData {
|
||||
let attr = SR1PartDataAttr::from_raw(&self, None, true);
|
||||
@ -534,7 +533,7 @@ pub mod ship {
|
||||
angle_v: self.angle_v.to_owned(),
|
||||
flip_x: i8_to_bool(self.flip_x.unwrap_or(0_i8)),
|
||||
flip_y: i8_to_bool(self.flip_y.unwrap_or(0_i8)),
|
||||
editor_angle: self.editor_angle.to_owned(),
|
||||
editor_angle: self.editor_angle.unwrap_or(0_i32),
|
||||
part_type,
|
||||
part_type_id: self.part_type_id.clone(),
|
||||
active: i8_to_bool(self.activated.unwrap_or(0_i8)),
|
||||
@ -576,11 +575,11 @@ pub mod ship {
|
||||
SR1Ship {
|
||||
name: name.unwrap_or("NewShip".to_string()),
|
||||
description: "".to_string(),
|
||||
version: self.version,
|
||||
version: self.version.unwrap_or(1_i32),
|
||||
parts,
|
||||
connections,
|
||||
lift_off: i8_to_bool(self.lift_off.to_owned()),
|
||||
touch_ground: i8_to_bool(self.touch_ground.to_owned()),
|
||||
touch_ground: option_i8_to_option_bool(self.touch_ground.to_owned()).unwrap_or(true),
|
||||
disconnected,
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,29 @@ pub mod sr1 {
|
||||
pub type IdType = i64;
|
||||
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)]
|
||||
#[inline]
|
||||
pub fn map_ptype_textures(ptype: String) -> String {
|
||||
@ -447,7 +470,7 @@ pub mod sr1 {
|
||||
id: self.id,
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
editor_angle: self.editor_angle,
|
||||
editor_angle: Some(self.editor_angle),
|
||||
angle: self.angle,
|
||||
angle_v: self.angle_v,
|
||||
flip_x: Some(bool_to_i8(self.flip_x)),
|
||||
@ -506,6 +529,8 @@ pub mod sr1 {
|
||||
}
|
||||
pos_box
|
||||
}
|
||||
|
||||
pub fn angle_degrees(&self) -> f64 { radians_map_to_degrees(self.angle) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -683,8 +708,15 @@ pub mod sr1 {
|
||||
return None;
|
||||
}
|
||||
// 解析为 RawShip
|
||||
let ship: RawShip = RawShip::from_file(file_name).unwrap();
|
||||
Some(ship.to_sr_ship(ship_name))
|
||||
let ship: Option<RawShip> = RawShip::from_file(file_name);
|
||||
match ship {
|
||||
Some(ship) => {
|
||||
// 解析为 SR1Ship
|
||||
let sr_ship = ship.to_sr_ship(ship_name);
|
||||
Some(sr_ship)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_part_list_to_part(&mut self, part_list: &SR1PartList) {
|
||||
@ -945,9 +977,9 @@ pub mod sr1 {
|
||||
RawShip {
|
||||
parts: RawParts { parts },
|
||||
connects: connections,
|
||||
version: self.version,
|
||||
version: Some(self.version),
|
||||
lift_off: bool_to_i8(self.lift_off),
|
||||
touch_ground: bool_to_i8(self.touch_ground),
|
||||
touch_ground: Some(bool_to_i8(self.touch_ground)),
|
||||
disconnected,
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ from Difficult_Rocket.api.mod import ModInfo
|
||||
from Difficult_Rocket.client import ClientWindow
|
||||
from Difficult_Rocket.api.types import Options, Version
|
||||
|
||||
DR_rust_version = Version("0.2.17.0") # DR_mod 的 Rust 编写部分的兼容版本
|
||||
DR_rust_version = Version("0.2.20.2") # DR_mod 的 Rust 编写部分的兼容版本
|
||||
|
||||
logger = logging.getLogger('client.dr_game')
|
||||
|
||||
@ -53,7 +53,7 @@ DR_mod_runtime = _DR_mod_runtime()
|
||||
class DR_mod(ModInfo): # NOQA
|
||||
mod_id = "difficult_rocket_mod"
|
||||
name = "Difficult Rocket mod"
|
||||
version = Version("0.3.1.2")
|
||||
version = Version("0.3.2.0")
|
||||
|
||||
writer = "shenjackyuanjie"
|
||||
link = "shenjack.top"
|
||||
@ -81,7 +81,7 @@ class DR_mod(ModInfo): # NOQA
|
||||
else:
|
||||
self.config.flush_option()
|
||||
logger.info("on_load")
|
||||
logger.info(self.as_markdown())
|
||||
logger.info(f"\n{self.as_markdown()}")
|
||||
return True
|
||||
|
||||
def on_client_start(self, game: Game, client: ClientWindow):
|
||||
|
@ -5,6 +5,7 @@
|
||||
# -------------------------------
|
||||
|
||||
# import math
|
||||
import re
|
||||
import time
|
||||
import random
|
||||
import logging
|
||||
@ -21,6 +22,7 @@ from pyglet.sprite import Sprite
|
||||
# from pyglet.image import Texture
|
||||
from pyglet.graphics import Batch, Group
|
||||
from pyglet.shapes import Line, Rectangle
|
||||
from pyglet.image import Framebuffer, Texture
|
||||
# pyglet OpenGL
|
||||
from pyglet.gl import glViewport
|
||||
|
||||
@ -40,44 +42,29 @@ if TYPE_CHECKING:
|
||||
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
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.level = logging.DEBUG
|
||||
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 SR1ShipRenderStatus(Options): # NOQA
|
||||
name = "SR1ShipRenderStatus"
|
||||
# main status
|
||||
draw_done: bool = False
|
||||
draw_call: bool = False
|
||||
update_call: bool = False
|
||||
focus: bool = True
|
||||
moving: bool = False
|
||||
|
||||
|
||||
class SR1ShipRender_Option(Options): # NOQA
|
||||
# debug option
|
||||
debug_d_pos: bool = False
|
||||
debug_mouse_pos: bool = False
|
||||
debug_mouse_d_pos: bool = False
|
||||
draw_size: Tuple[int, int] = (100, 100)
|
||||
# debug status
|
||||
draw_d_pos: bool = False
|
||||
draw_mouse_pos: bool = False
|
||||
draw_mouse_d_pos: bool = False
|
||||
|
||||
|
||||
class SR1ShipRender(BaseScreen):
|
||||
@ -89,14 +76,16 @@ class SR1ShipRender(BaseScreen):
|
||||
self.logger = logger
|
||||
logger.info(sr_tr().mod.info.setup.start())
|
||||
load_start_time = time.time_ns()
|
||||
self.rendered = False
|
||||
self.focus = True
|
||||
self.need_draw = False
|
||||
self.drawing = False
|
||||
self.need_update_parts = False
|
||||
self.render_option = SR1ShipRender_Option()
|
||||
# status
|
||||
self.status = SR1ShipRenderStatus()
|
||||
|
||||
self.dx = 0
|
||||
self.dy = 0
|
||||
self.width = main_window.width - 100
|
||||
self.height = main_window.height - 100
|
||||
self.buffer = Framebuffer()
|
||||
self.render_texture = Texture.create(self.width, self.height)
|
||||
self.buffer.attach_texture(self.render_texture)
|
||||
|
||||
self.main_batch = Batch()
|
||||
self.part_group = Group(10, parent=main_window.main_group)
|
||||
@ -108,36 +97,45 @@ class SR1ShipRender(BaseScreen):
|
||||
batch=self.main_batch, group=Group(5, parent=self.part_group))
|
||||
self.render_d_line = Line(0, 0, 0, 0, width=5, color=(200, 200, 10, 255),
|
||||
batch=self.main_batch, group=Group(5, parent=self.part_group))
|
||||
self.render_d_line.visible = self.render_option.debug_mouse_d_pos
|
||||
self.render_d_line.visible = self.status.draw_d_pos
|
||||
self.render_d_label = Label('debug label NODATA', font_name=Fonts.微软等宽无线,
|
||||
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.status.draw_d_pos
|
||||
self.camera = CenterCamera(main_window, min_zoom=(1 / 2) ** 10, max_zoom=10)
|
||||
|
||||
# Optional data
|
||||
self.textures: SR1Textures = SR1Textures()
|
||||
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.ship_name: Optional[str] = None
|
||||
|
||||
# List/Dict data
|
||||
self.part_data: Dict[int, SR1PartData] = {}
|
||||
self.parts_sprite: Dict[int, Sprite] = {}
|
||||
self.parts_sprite: Dict[int, List[Sprite]] = {}
|
||||
self.part_box_dict: Dict[int, Rectangle] = {}
|
||||
self.part_line_box: Dict[int, 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:
|
||||
self.rust_parts = None
|
||||
self.part_list_rs = SR1PartList_rs('assets/builtin/PartList.xml', 'builtin_part_list')
|
||||
|
||||
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))
|
||||
|
||||
@property
|
||||
def size(self) -> Tuple[int, int]:
|
||||
""" 渲染器的渲染大小 """
|
||||
return self.width, self.height
|
||||
|
||||
@size.setter
|
||||
def size(self, value: Tuple[int, int]):
|
||||
if not self.width == value[0] or not self.height == value[1]:
|
||||
self.width, self.height = value
|
||||
self.render_texture = Texture.create(self.width, self.height)
|
||||
self.buffer.attach_texture(self.render_texture)
|
||||
|
||||
def load_xml(self, file_path: str) -> bool:
|
||||
"""
|
||||
加载 xml 文件
|
||||
@ -147,58 +145,46 @@ class SR1ShipRender(BaseScreen):
|
||||
try:
|
||||
start_time = time.time_ns()
|
||||
logger.info(sr_tr().sr1.ship.xml.loading().format(file_path))
|
||||
cache_doc = parse(file_path)
|
||||
self.xml_doc = cache_doc
|
||||
self.xml_root = self.xml_doc.getroot()
|
||||
self.xml_name = file_path
|
||||
self.ship_name = file_path.split('/')[-1].split('.')[0]
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
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_time().format(
|
||||
(time.time_ns() - start_time) / 1000000000))
|
||||
return True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
self.logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
def load_textures(self):
|
||||
"""
|
||||
初始化纹理加载
|
||||
:return:
|
||||
"""
|
||||
self.textures = SR1Textures()
|
||||
|
||||
def gen_sprite(self, part_datas: Dict[int, SR1PartData], each_count: int = 100) -> Generator:
|
||||
def gen_sprite(self, each_count: int = 100) -> Generator:
|
||||
"""
|
||||
生成 sprite
|
||||
通过生成器减少一次性渲染的压力
|
||||
:param part_datas: 所有的部件数据
|
||||
:param each_count: 每次生成的数量 (默认 100) (过大会导致卡顿)
|
||||
:return: 生成器
|
||||
"""
|
||||
count = 0
|
||||
self.drawing = True
|
||||
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 和这里的屏幕像素的关系(OK
|
||||
# 旋转啥的不是大问题, 我找你要那个渲染代码就是要 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
|
||||
self.status.draw_done = False
|
||||
# rust 渲染
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
cache = self.rust_ship.as_dict()
|
||||
for p_id, parts in cache.items():
|
||||
p_id: int
|
||||
parts: List[Tuple[SR1PartType_rs, SR1PartData_rs]]
|
||||
part_group = Group(2, parent=self.part_group)
|
||||
batch = []
|
||||
for p_type, p_data in parts:
|
||||
part_sprite = Sprite(img=self.textures.get_texture(map_ptype_textures(p_data.part_type_id)),
|
||||
x=p_data.x * 60, y=p_data.y * 60, z=random.random(),
|
||||
batch=self.main_batch, group=part_group)
|
||||
part_sprite.rotation = p_data.angle_r
|
||||
part_sprite.scale_x = -1 if p_data.flip_x else 1
|
||||
part_sprite.scale_y = -1 if p_data.flip_y else 1
|
||||
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
batch.append(part_sprite)
|
||||
line_box_group = Group(6, parent=self.part_group)
|
||||
part_debug_box = self.rust_ship.get_part_box(part.id)
|
||||
part_debug_box = self.rust_ship.get_part_box(p_id)
|
||||
if part_debug_box:
|
||||
# 线框
|
||||
part_line_box = []
|
||||
@ -217,17 +203,17 @@ class SR1ShipRender(BaseScreen):
|
||||
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,
|
||||
batch=self.main_batch, width=width, color=color, group=line_box_group))
|
||||
self.part_line_box[part.id] = part_line_box
|
||||
count += 1
|
||||
if count >= each_count:
|
||||
count = 0
|
||||
yield count
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
self.part_line_box[p_id] = part_line_box
|
||||
self.parts_sprite[p_id] = batch
|
||||
count += 1
|
||||
if count >= each_count:
|
||||
count = 0
|
||||
yield
|
||||
connect_line_group = Group(7, parent=self.part_group)
|
||||
for connect in self.rust_ship.connection:
|
||||
# 连接线
|
||||
parent_part_data = self.part_data[connect[2]]
|
||||
child_part_data = self.part_data[connect[3]]
|
||||
parent_part_data = cache[connect[2]][0][1]
|
||||
child_part_data = cache[connect[3]][0][1]
|
||||
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,
|
||||
x2=child_part_data.x * 60, y2=child_part_data.y * 60,
|
||||
@ -237,84 +223,105 @@ class SR1ShipRender(BaseScreen):
|
||||
if count >= each_count * 3:
|
||||
count = 0
|
||||
yield count
|
||||
self.drawing = False
|
||||
|
||||
# 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.status.draw_done = True
|
||||
raise GeneratorExit
|
||||
|
||||
def render_ship(self):
|
||||
"""
|
||||
渲染船
|
||||
"""
|
||||
if self.textures is None:
|
||||
self.load_textures()
|
||||
logger.info(sr_tr().sr1.ship.ship.load().format(self.xml_name))
|
||||
self.status.draw_done = False
|
||||
logger.info(sr_tr().sr1.ship.ship.load().format(self.ship_name))
|
||||
start_time = time.perf_counter_ns()
|
||||
self.part_data: Dict[int, SR1PartData] = {}
|
||||
self.parts_sprite: Dict[int, Sprite] = {}
|
||||
self.part_line_box = {}
|
||||
self.part_line_list = []
|
||||
self.camera.zoom = 1.0
|
||||
self.camera.dx = 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:
|
||||
self.gen_draw = self.gen_sprite(self.part_data)
|
||||
next(self.gen_draw)
|
||||
except GeneratorExit:
|
||||
self.drawing = False
|
||||
self.need_draw = False
|
||||
self.gen_draw = self.gen_sprite()
|
||||
if not self.status.draw_done:
|
||||
next(self.gen_draw)
|
||||
except (GeneratorExit, StopIteration):
|
||||
self.status.draw_done = True
|
||||
self.status.draw_call = False
|
||||
full_mass = 0
|
||||
if DR_mod_runtime.use_DR_rust:
|
||||
for part in self.part_data:
|
||||
full_mass += self.part_list_rs.get_part_type(self.part_data[part].p_type).mass * 500
|
||||
full_mass = self.rust_ship.mass
|
||||
logger.info(sr_tr().sr1.ship.ship.load_time().format(
|
||||
(time.perf_counter_ns() - start_time) / 1000000000))
|
||||
logger.info(sr_tr().sr1.ship.ship.info().format(
|
||||
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
|
||||
len(self.rust_ship.as_list()),
|
||||
f'{full_mass}kg' if DR_mod_runtime.use_DR_rust else sr_tr().game.require_DR_rs()))
|
||||
|
||||
def draw_batch(self, window: "ClientWindow"):
|
||||
if self.rendered:
|
||||
if self.status.draw_done:
|
||||
self.render_d_label.text = f'x: {self.camera.dx} y: {self.camera.dy}'
|
||||
self.render_d_label.position = self.camera.dx + (self.window_pointer.width / 2), self.camera.dy + (
|
||||
self.window_pointer.height / 2) + 10, 0 # 0 for z
|
||||
self.render_d_line.x2 = self.camera.dx
|
||||
self.render_d_line.y2 = self.camera.dy
|
||||
self.buffer.bind()
|
||||
window.clear()
|
||||
with self.camera:
|
||||
# glViewport(int(self.camera.dx), int(self.camera.dy), window.width // 2, window.height // 2)
|
||||
self.main_batch.draw()
|
||||
# glViewport(0, 0, window.width, window.height)
|
||||
self.buffer.unbind()
|
||||
self.render_texture.blit(x=0, y=0, z=0, width=self.width, height=self.height)
|
||||
|
||||
def on_draw(self, window: "ClientWindow"):
|
||||
if self.need_draw:
|
||||
if self.status.draw_call:
|
||||
self.render_ship()
|
||||
|
||||
if self.drawing:
|
||||
if not self.status.draw_done:
|
||||
try:
|
||||
next(self.gen_draw)
|
||||
except GeneratorExit:
|
||||
self.drawing = False
|
||||
except (GeneratorExit, StopIteration):
|
||||
self.status.draw_done = True
|
||||
self.logger.info(sr_tr().sr1.ship.ship.render.done())
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.debug_label.draw()
|
||||
|
||||
def on_resize(self, width: int, height: int, window: "ClientWindow"):
|
||||
self.debug_label.y = height - 100
|
||||
if not self.rendered:
|
||||
if not self.status.draw_done:
|
||||
return
|
||||
self.render_d_line.x2 = width // 2
|
||||
self.render_d_line.y2 = height // 2
|
||||
self.render_option.draw_size = (width, height)
|
||||
self.size = width - 100, height - 100
|
||||
|
||||
def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int, window: "ClientWindow"):
|
||||
if not self.rendered:
|
||||
if not self.status.draw_done:
|
||||
return
|
||||
mouse_dx = x - (window.width / 2)
|
||||
mouse_dy = y - (window.height / 2)
|
||||
@ -348,15 +355,16 @@ class SR1ShipRender(BaseScreen):
|
||||
self.camera.dy = 0
|
||||
self.window_pointer.view = Vec4()
|
||||
else:
|
||||
self.need_draw = True
|
||||
self.status.draw_call = True
|
||||
print('应该渲染飞船的')
|
||||
|
||||
elif command.find('debug'):
|
||||
if command.find('delta'):
|
||||
self.render_d_line.visible = not self.render_d_line.visible
|
||||
self.render_option.debug_mouse_d_pos = self.render_d_line.visible
|
||||
self.logger.info(f'sr1 mouse {self.render_option.debug_mouse_d_pos}')
|
||||
self.status.draw_mouse_d_pos = self.render_d_line.visible
|
||||
self.logger.info(f'sr1 mouse {self.status.draw_mouse_d_pos}')
|
||||
elif command.find('ship'):
|
||||
if self.rendered:
|
||||
if self.status.draw_done:
|
||||
for index, sprite in self.parts_sprite.items():
|
||||
sprite.visible = not sprite.visible
|
||||
|
||||
@ -368,8 +376,7 @@ class SR1ShipRender(BaseScreen):
|
||||
:param window:
|
||||
:return:
|
||||
"""
|
||||
from pyglet.gl import GLubyte, GL_RGBA, GL_UNSIGNED_BYTE, \
|
||||
glReadPixels
|
||||
from pyglet.gl import GLubyte, GL_RGBA, GL_UNSIGNED_BYTE, glReadPixels
|
||||
import pyglet
|
||||
format_str = "RGBA"
|
||||
buf = (GLubyte * (len(format_str) * window.width * window.height))()
|
||||
@ -379,13 +386,13 @@ class SR1ShipRender(BaseScreen):
|
||||
image_data = screenshot(self.window_pointer)
|
||||
image_data.save('test.png')
|
||||
elif command.find('gen_img'):
|
||||
if not self.rendered:
|
||||
if not self.status.draw_done:
|
||||
return
|
||||
if not DR_mod_runtime.use_DR_rust:
|
||||
# 这个功能依赖于 DR rs (简称,我懒得在Python端实现)
|
||||
return
|
||||
img_box = self.rust_ship.img_pos
|
||||
img_size = (img_box[2] - img_box[0] + 1000, img_box[3] - img_box[1] + 1000)
|
||||
img_size = (img_box[2] - img_box[0], img_box[3] - img_box[1])
|
||||
# 中心点是左上角坐标
|
||||
img_center = (abs(img_box[0]), abs(img_box[3]))
|
||||
try:
|
||||
@ -395,29 +402,37 @@ class SR1ShipRender(BaseScreen):
|
||||
print('PIL not found')
|
||||
return
|
||||
img = Image.new('RGBA', img_size)
|
||||
for part, sprite in self.parts_sprite.items():
|
||||
sprite_img = sprite.image
|
||||
print(f"sprite_img: {sprite_img} {sprite_img.width} {sprite_img.height}")
|
||||
img_data = sprite_img.get_image_data()
|
||||
fmt = img_data.format
|
||||
if fmt != 'RGB':
|
||||
fmt = 'RGBA'
|
||||
pitch = -(img_data.width * len(fmt))
|
||||
pil_image = Image.frombytes(fmt, (img_data.width, img_data.height), img_data.get_data(fmt, pitch))
|
||||
pil_image = pil_image.rotate(SR1Rotation.get_rotation(self.part_data[part].angle), expand=True)
|
||||
if self.part_data[part].flip_y:
|
||||
pil_image.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
if self.part_data[part].flip_x:
|
||||
pil_image.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
img.paste(pil_image, (
|
||||
int(self.part_data[part].x * 60 + img_center[0]),
|
||||
int(-self.part_data[part].y * 60 + img_center[1])),
|
||||
pil_image)
|
||||
part_data = self.rust_ship.as_dict()
|
||||
for part, sprites in self.parts_sprite.items():
|
||||
for index, sprite in enumerate(sprites):
|
||||
sprite_img = sprite.image
|
||||
print(
|
||||
f"sprite_img: {sprite_img} {part_data[part][index][1].x * 60} {part_data[part][index][1].y * 60}")
|
||||
img_data = sprite_img.get_image_data()
|
||||
fmt = img_data.format
|
||||
if fmt != 'RGB':
|
||||
fmt = 'RGBA'
|
||||
pitch = -(img_data.width * len(fmt))
|
||||
pil_image = Image.frombytes(fmt, (img_data.width, img_data.height), img_data.get_data(fmt, pitch))
|
||||
|
||||
pil_image = pil_image.rotate(-SR1Rotation.get_rotation(part_data[part][index][1].angle),
|
||||
expand=True)
|
||||
|
||||
if part_data[part][index][1].flip_y:
|
||||
pil_image.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
if part_data[part][index][1].flip_x:
|
||||
pil_image.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
|
||||
img.paste(pil_image, (
|
||||
int(part_data[part][index][1].x * 60 + img_center[0]),
|
||||
int(-part_data[part][index][1].y * 60 + img_center[1])))
|
||||
|
||||
img.show("???")
|
||||
img.save(f'test{time.time()}.png', 'PNG')
|
||||
|
||||
elif command.find('test'):
|
||||
if command.find('save'):
|
||||
if not self.rendered:
|
||||
if not self.status.draw_done:
|
||||
return
|
||||
if not DR_mod_runtime.use_DR_rust:
|
||||
return
|
||||
@ -427,21 +442,35 @@ class SR1ShipRender(BaseScreen):
|
||||
glViewport(0, 0, 1000, 1000)
|
||||
|
||||
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
|
||||
if not self.focus:
|
||||
if not self.status.focus:
|
||||
return
|
||||
self.camera.dx += dx
|
||||
self.camera.dy += dy
|
||||
self.need_update_parts = True
|
||||
# self.update_parts()
|
||||
self.status.update_call = True
|
||||
|
||||
def on_file_drop(self, x: int, y: int, paths: List[str], window: "ClientWindow"):
|
||||
for path in paths:
|
||||
if self.load_xml(path): # 加载成功一个就停下
|
||||
break
|
||||
self.render_ship()
|
||||
if len(paths) > 1:
|
||||
for path in paths:
|
||||
try:
|
||||
self.load_xml(path)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
if Path(paths[0]).is_dir():
|
||||
for path in Path(paths[0]).glob('*.xml'):
|
||||
try:
|
||||
self.load_xml(str(path))
|
||||
except ValueError:
|
||||
traceback.print_exc()
|
||||
if self.load_xml(paths[0]):
|
||||
self.render_ship()
|
||||
# for path in paths:
|
||||
# if self.load_xml(path): # 加载成功一个就停下
|
||||
# break
|
||||
# self.render_ship()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from objprint import op
|
||||
|
||||
op(SR1ShipRender_Option())
|
||||
op(SR1ShipRenderStatus())
|
||||
|
@ -33,10 +33,8 @@ class SR1PartData:
|
||||
|
||||
class SR1Textures(Options):
|
||||
""" 存储 sr1 的材质 img """
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.flush_option()
|
||||
for image_name in self.cached_options:
|
||||
def load_file(self, **kwargs):
|
||||
for image_name in self.flush_option():
|
||||
img = load(f'assets/textures/parts/{image_name}.png')
|
||||
img.anchor_x = img.width // 2
|
||||
img.anchor_y = img.height // 2
|
||||
@ -49,8 +47,14 @@ class SR1Textures(Options):
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
assert name in self.cached_options
|
||||
return self.cached_options.get(name)
|
||||
if name in self.cached_options:
|
||||
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
|
||||
Beam: AbstractImage = None
|
||||
@ -134,6 +138,11 @@ class SR1Rotation(Options):
|
||||
|
||||
@classmethod
|
||||
def get_rotation(cls, radian: float) -> float:
|
||||
"""
|
||||
实际上就是将弧度转换为角度 (同时自带一个映射表)
|
||||
:param radian:
|
||||
:return:
|
||||
"""
|
||||
if radian in cls.radian_angle_map:
|
||||
return cls.radian_angle_map[radian]
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user