1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

Fix battle arena ending at 96% base charge, fix part of #61

This commit is contained in:
NG (Graham)
2026-01-18 12:39:58 -05:00
parent 5fd4acdf62
commit ff0588ada1
10 changed files with 805 additions and 93 deletions

View File

@@ -0,0 +1,387 @@
pub const CUBE_CONNECTIONS: &[CubeConnections] = &[
CubeConnections {
id: 606866102, // Protonium Clasp
connections: &[
CubeConnection {
direction: (0, 0, 1),
position: (0, 38, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 48, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 58, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 68, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 78, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 88, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 98, 25),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 108, 25),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 38, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 48, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 58, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 68, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 78, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 88, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 98, -24),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 108, -24),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 38, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 48, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 58, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 68, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 78, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 88, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 98, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (25, 108, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 38, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 48, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 58, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 68, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 78, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 88, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 98, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-24, 108, 0),
},
/*CubeConnection { // assumed by game client (but not useful)
direction: (0, -1, 0),
position: (0, 0, 0),
},*/
],
},
CubeConnections {
id: 3950293873, // Protonium Crystal
connections: &[
// -y (bottom)
CubeConnection { // assumed by game client
direction: (0, -1, 0),
position: (0, 0, 0),
},
CubeConnection {
direction: (0, -1, 0),
position: (1, 0, 0),
},
CubeConnection {
direction: (0, -1, 0),
position: (0, 0, 1),
},
CubeConnection {
direction: (0, -1, 0),
position: (1, 0, 1),
},
// +y (top)
CubeConnection {
direction: (0, 1, 0),
position: (0, 9, 0),
},
CubeConnection {
direction: (0, 1, 0),
position: (1, 9, 0),
},
CubeConnection {
direction: (0, 1, 0),
position: (0, 9, 1),
},
CubeConnection {
direction: (0, 1, 0),
position: (1, 9, 1),
},
// -x (right?)
CubeConnection {
direction: (1, 0, 0),
position: (5, 4, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (5, 4, 1),
},
CubeConnection {
direction: (1, 0, 0),
position: (5, 5, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (5, 5, 1),
},
// -x (left?)
CubeConnection {
direction: (-1, 0, 0),
position: (-4, 4, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-4, 4, 1),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-4, 5, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-4, 5, 1),
},
// +z (back)
CubeConnection {
direction: (0, 0, 1),
position: (0, 4, 5),
},
CubeConnection {
direction: (0, 0, 1),
position: (1, 4, 5),
},
CubeConnection {
direction: (0, 0, 1),
position: (0, 5, 5),
},
CubeConnection {
direction: (0, 0, 1),
position: (1, 5, 5),
},
// -z (front)
CubeConnection {
direction: (0, 0, -1),
position: (0, 4, -4),
},
CubeConnection {
direction: (0, 0, -1),
position: (1, 4, -4),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 5, -4),
},
CubeConnection {
direction: (0, 0, -1),
position: (1, 5, -4),
},
// diagonal faces
CubeConnection {
direction: (-1, 0, 0),
position: (-2, 2, -2),
},
CubeConnection {
direction: (0, -1, 0),
position: (-2, 2, -2),
},
CubeConnection {
direction: (1, 0, 0),
position: (3, 2, -2),
},
CubeConnection {
direction: (0, -1, 0),
position: (3, 2, -2),
},
CubeConnection {
direction: (0, 0, -1),
position: (3, 2, -2),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-2, 2, 3),
},
CubeConnection {
direction: (0, -1, 0),
position: (-2, 2, 3),
},
CubeConnection {
direction: (0, 0, 1),
position: (-2, 2, 3),
},
CubeConnection {
direction: (1, 0, 0),
position: (3, 2, 3),
},
CubeConnection {
direction: (0, -1, 0),
position: (3, 2, 3),
},
CubeConnection {
direction: (0, 0, 1),
position: (3, 2, 3),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-2, 6, -2),
},
CubeConnection {
direction: (0, 1, 0),
position: (-2, 6, -2),
},
CubeConnection {
direction: (0, 0, -1),
position: (-2, 6, -2),
},
CubeConnection {
direction: (1, 0, 0),
position: (3, 6, -2),
},
CubeConnection {
direction: (0, 1, 0),
position: (3, 6, -2),
},
CubeConnection {
direction: (0, 0, -1),
position: (3, 6, -2),
},
CubeConnection {
direction: (-1, 0, 0),
position: (-2, 6, 3),
},
CubeConnection {
direction: (0, 1, 0),
position: (-2, 6, 3),
},
CubeConnection {
direction: (0, 0, 1),
position: (-2, 6, 3),
},
CubeConnection {
direction: (1, 0, 0),
position: (3, 6, 3),
},
CubeConnection {
direction: (0, 1, 0),
position: (3, 6, 3),
},
CubeConnection {
direction: (0, 0, 1),
position: (3, 6, 3),
},
],
},
CubeConnections {
id: 0, // (default)
connections: &[
CubeConnection {
direction: (0, 0, 1),
position: (0, 0, 0),
},
CubeConnection {
direction: (0, 1, 0),
position: (0, 0, 0),
},
CubeConnection {
direction: (1, 0, 0),
position: (0, 0, 0),
},
CubeConnection {
direction: (0, 0, -1),
position: (0, 0, 0),
},
CubeConnection {
direction: (0, -1, 0),
position: (0, 0, 0),
},
CubeConnection {
direction: (-1, 0, 0),
position: (0, 0, 0),
},
],
},
];
#[derive(Copy, Clone)]
pub struct CubeConnections {
pub id: u32,
pub connections: &'static [CubeConnection],
}
#[derive(Copy, Clone)]
pub struct CubeConnection {
pub direction: (i8, i8, i8),
pub position: (i8, i8, i8),
}

View File

@@ -1,3 +1,6 @@
const CRYSTAL_ID: u32 = 3950293873;
const CLASP_ID: u32 = 606866102;
pub struct CubeLocationsParser;
pub struct CubeLocationInfo {
@@ -68,6 +71,34 @@ impl CubeLocationsParser {
.collect()
}
fn locations_sorted_by_distance_from_point_reward_inlines(cubes: &[super::parser::Cube], point: (u8, u8, u8), locations_of_id: u32, inline_div: f32) -> Vec<CubeLocationInfo> {
let target_x = point.0 as f32;
let target_y = point.1 as f32;
let target_z = point.2 as f32;
let mut relevant_cubes: Vec<(f32, CubeLocationInfo)> = cubes.iter()
.filter(|x| x.id == locations_of_id)
.map(|cube| {
let is_inline = cube.x.wrapping_sub(point.0) == 0 || cube.z.wrapping_sub(point.2) == 0;
let distance = (
(cube.x as f32 - target_x).powi(2)
+ (cube.y as f32 - target_y).powi(2)
+ (cube.z as f32 - target_z).powi(2)
).sqrt();
let distance = if is_inline { distance / inline_div } else { distance };
(distance, CubeLocationInfo {
x: cube.x,
y: cube.y,
z: cube.z,
extras: cube.orientation,
})
})
.collect();
relevant_cubes.sort_by_key(|(distance, _)| (distance * 1_000_000.0) as i64);
relevant_cubes.into_iter()
.map(|(_, cube)| cube)
.collect()
}
pub fn locations_of_by_distance_to_first(&self, r: &mut dyn std::io::Read, locations_of_id: u32, distance_to_id: u32) -> Vec<CubeLocationInfo> {
match super::parser::Cube::parse_list(r) {
Ok(cubes) => {
@@ -113,4 +144,195 @@ impl CubeLocationsParser {
}
}
}
pub fn locations_of_by_distance_from_center(&self, r: &mut dyn std::io::Read, locations_of_id: u32) -> Vec<CubeLocationInfo> {
match super::parser::Cube::parse_list(r) {
Ok(cubes) => {
let mut total = 0;
let mut total_x = 0;
let mut total_y = 0;
let mut total_z = 0;
for cube in cubes.iter() {
if cube.id == locations_of_id {
total += 1;
total_x += cube.x as usize;
total_y += cube.y as usize;
total_z += cube.z as usize;
}
}
let center = (total_x / total, total_y / total, total_z / total);
let center = (center.0 as u8, center.1 as u8, center.2 as u8);
Self::locations_sorted_by_distance_from_point_reward_inlines(
&cubes,
center,
locations_of_id,
4.0,
)
}
Err(e) => {
log::error!("Failed to parse cube data to find cube locations: {}", e);
Vec::default()
}
}
}
pub fn locations_of_reactor_sort(&self, r: &mut dyn std::io::Read) -> Vec<CubeLocationInfo> {
use super::{CUBE_CONNECTIONS, CUBE_ROTATIONS};
match super::parser::Cube::parse_list(r) {
Ok(cubes) => {
// get connection data
let location_cube_connections = if let Some(conn) = CUBE_CONNECTIONS.iter().find(|x| x.id == CRYSTAL_ID) {
conn
} else {
CUBE_CONNECTIONS.iter().find(|x| x.id == 0).unwrap()
};
let connected_to_connections = if let Some(conn) = CUBE_CONNECTIONS.iter().find(|x| x.id == CLASP_ID) {
conn
} else {
CUBE_CONNECTIONS.iter().find(|x| x.id == 0).unwrap()
};
// calculate connect target connection points
let mut abs_connected_to = None;
let mut target_cube_loc = None;
for cube in cubes.iter() {
if cube.id == CLASP_ID {
let rot_index = cube.orientation & 0b01111111;
let rot = &CUBE_ROTATIONS[rot_index as usize];
log::trace!("Calculating target connection points");
abs_connected_to = Some(calculate_absolute_connections(connected_to_connections, rot, (cube.x, cube.y, cube.z)));
target_cube_loc = Some(CubeLocationInfo {
x: cube.x,
y: cube.y,
z: cube.z,
extras: cube.orientation,
});
break;
}
}
let mut available_faces = if let Some(abs_connected_to) = abs_connected_to {
abs_connected_to
} else {
log::error!("Failed to find cube with id {} to calculate connections to", CLASP_ID);
return Vec::default();
};
let target_cube_loc = target_cube_loc.unwrap();
// find cubes connected to target (or to another relevant cube connected to the target)
let mut to_be_sorted = std::collections::HashMap::new();
for (i, cube) in cubes.iter().enumerate() {
if cube.id == CRYSTAL_ID {
let rot_index = cube.orientation & 0b01111111;
let rot = &CUBE_ROTATIONS[rot_index as usize];
log::trace!("Calculating location {} connection points", i);
let calc_conns = calculate_absolute_connections(location_cube_connections, rot, (cube.x, cube.y, cube.z));
to_be_sorted.insert(i, calc_conns);
}
}
let mut sorted = Vec::with_capacity(to_be_sorted.len() + 1);
sorted.push(target_cube_loc);
let mut iteration = 0;
let mut random = rand::rng();
let mut to_be_released = Vec::new();
while !to_be_sorted.is_empty() && iteration < 32 {
for (cube_i, calc_conns) in to_be_sorted.iter() {
let connection = is_sharing_connection(calc_conns, &available_faces)
.and_then(|(cube_face_i, face_available_i)| {
use rand::Rng;
if face_available_i < connected_to_connections.connections.len() && sorted.len() < connected_to_connections.connections.len() {
// prioritize finding all target connections first
Some((cube_face_i, face_available_i))
} else if iteration < 3 || (face_available_i >= connected_to_connections.connections.len() && sorted.len() < connected_to_connections.connections.len()) {
let random_face = random.random_range(0..calc_conns.len());
if cube_face_i >= random_face {
Some((cube_face_i, face_available_i))
} else {
None
}
} else {
Some((cube_face_i, face_available_i))
}
});
if connection.is_some() {
to_be_released.push(*cube_i);
let cube = &cubes[*cube_i];
sorted.push(CubeLocationInfo {
x: cube.x,
y: cube.y,
z: cube.z,
extras: cube.orientation,
});
}
}
for to_release in to_be_released.iter() {
if let Some(mut new_faces) = to_be_sorted.remove(to_release) {
available_faces.append(&mut new_faces);
}
}
// guarantee this will not endlessly try to find just target connections
// (this moves on to considering already-connected locations_of_id cubes)
log::debug!("Cube connection iteration {} found {} new connections", iteration, to_be_released.len());
to_be_released.clear();
iteration += 1;
}
if to_be_sorted.is_empty() {
log::debug!("Solved cube connections order in {} iterations", iteration);
} else {
log::warn!("Failed to solve cube connections order after {} iterations (returning incomplete list)", iteration);
}
sorted
}
Err(e) => {
log::error!("Failed to parse cube data to find cube locations by connections: {}", e);
Vec::default()
}
}
}
}
#[derive(Hash, Eq, PartialEq, Clone, Copy)]
struct AbsoluteConnection {
direction: (i8, i8, i8),
position: (u8, u8, u8),
}
fn calculate_absolute_connections(conns: &super::connections::CubeConnections, rot: &num_quaternion::Quaternion<f32>, pos: (u8, u8, u8)) -> Vec<AbsoluteConnection> {
let unit_rot = rot.normalize().unwrap();
let mut abs_conns = Vec::with_capacity(conns.connections.len());
for conn in conns.connections.iter() {
let rotated_pos = unit_rot.rotate_vector([
conn.position.0 as f32,
conn.position.1 as f32,
conn.position.2 as f32
]);
let rotated_dir = unit_rot.rotate_vector([
conn.direction.0 as f32,
conn.direction.1 as f32,
conn.direction.2 as f32
]);
let new_conn = AbsoluteConnection {
direction: (rotated_dir[0] as i8, rotated_dir[1] as i8, rotated_dir[2] as i8),
position: (
pos.0.saturating_add_signed(rotated_pos[0] as i8).saturating_add_signed(rotated_dir[0].clamp(0.0, 1.0) as i8),
pos.1.saturating_add_signed(rotated_pos[1] as i8).saturating_add_signed(rotated_dir[1].clamp(0.0, 1.0) as i8),
pos.2.saturating_add_signed(rotated_pos[2] as i8).saturating_add_signed(rotated_dir[2].clamp(0.0, 1.0) as i8),
),
};
log::trace!("absolute connection point {:?}", new_conn.position);
abs_conns.push(new_conn);
}
abs_conns
}
fn is_sharing_connection(a: &[AbsoluteConnection], b: &[AbsoluteConnection]) -> Option<(usize, usize)> {
for (i_a, conn_a) in a.iter().enumerate() {
for (i_b, conn_b) in b.iter().enumerate() {
if conn_b.position == conn_a.position
&& conn_b.direction.0 == -conn_a.direction.0
&& conn_b.direction.1 == -conn_a.direction.1
&& conn_b.direction.2 == -conn_a.direction.2 {
return Some((i_a, i_b));
}
}
}
None
}

View File

@@ -1,4 +1,4 @@
mod parser;
mod parser;
mod weapon_list;
pub use weapon_list::WeaponListParser;
@@ -12,6 +12,12 @@ pub use locations_of::{CubeLocationsParser, CubeLocationInfo};
mod offsetter;
pub use offsetter::OffsetParser;
mod connections;
pub use connections::CUBE_CONNECTIONS;
mod rotations;
pub use rotations::CUBE_ROTATIONS;
//pub mod prefabs;
pub struct CubeParsers {

View File

@@ -12,15 +12,27 @@ impl OffsetParser {
let colours = &mut colours[4..];
for cube in cubes.chunks_mut(8) {
// bytes 4, 5, 6 are x, y, z (respectively)
cube[4] = (cube[4] as i16 + offset.0) as _;
cube[5] = (cube[5] as i16 + offset.1) as _;
cube[6] = (cube[6] as i16 + offset.2) as _;
cube[4] = (cube[4] as u16).saturating_add_signed(offset.0) as _;
cube[5] = (cube[5] as u16).saturating_add_signed(offset.1) as _;
cube[6] = (cube[6] as u16).saturating_add_signed(offset.2) as _;
}
for colour in colours.chunks_mut(4) {
// last 3 bytes are x, y, z (respectively)
colour[1] = (colour[1] as i16 + offset.0) as _;
colour[2] = (colour[2] as i16 + offset.1) as _;
colour[3] = (colour[3] as i16 + offset.2) as _;
colour[1] = (colour[1] as u16).saturating_add_signed(offset.0) as _;
colour[2] = (colour[2] as u16).saturating_add_signed(offset.1) as _;
colour[3] = (colour[3] as u16).saturating_add_signed(offset.2) as _;
}
}
pub fn offset_inplace_with_id(&self, cubes: &mut [u8], offset: (i8, i8, i8), target_id: u32) {
let cubes = &mut cubes[4..];
for cube in cubes.chunks_mut(8) {
let id = u32::from_le_bytes([cube[0], cube[1], cube[2], cube[3]]);
if id != target_id { continue; }
// bytes 4, 5, 6 are x, y, z (respectively)
cube[4] = cube[4].saturating_add_signed(offset.0);
cube[5] = cube[5].saturating_add_signed(offset.1);
cube[6] = cube[6].saturating_add_signed(offset.2);
}
}
}

View File

@@ -0,0 +1,27 @@
#[allow(clippy::approx_constant)]
pub const CUBE_ROTATIONS: [num_quaternion::Quaternion<f32>; 24] = [
num_quaternion::Quaternion::new(1.0, 0.0, 0.0, 0.0),
num_quaternion::Quaternion::new(0.7071068, 0.0, 0.0, 0.7071068),
num_quaternion::Quaternion::new(0.0, 0.0, 0.0, 1.0),
num_quaternion::Quaternion::new(-0.7071068, 0.0, 0.0, 0.7071068),
num_quaternion::Quaternion::new(0.7071068, 0.0, 0.7071068, 0.0),
num_quaternion::Quaternion::new(0.5, 0.5, 0.5, 0.5),
num_quaternion::Quaternion::new(0.5, -0.5, -0.5, -0.5),
num_quaternion::Quaternion::new(-0.5, 0.5, -0.5, 0.5),
num_quaternion::Quaternion::new(-0.5, 0.5, 0.5, -0.5),
num_quaternion::Quaternion::new(0.5, 0.5, -0.5, -0.5),
num_quaternion::Quaternion::new(0.5, 0.5, -0.5, 0.5),
num_quaternion::Quaternion::new(-0.5, -0.5, -0.5, 0.5),
num_quaternion::Quaternion::new(-0.5, 0.5, -0.5, -0.5),
num_quaternion::Quaternion::new(0.0, 0.7071068, 0.0, 0.7071068),
num_quaternion::Quaternion::new(0.0, 0.0, 1.0, 0.0),
num_quaternion::Quaternion::new(0.0, 0.7071068, 0.7071068, 0.0),
num_quaternion::Quaternion::new(0.0, 1.0, 0.0, 0.0),
num_quaternion::Quaternion::new(0.0, 0.7071068, -0.7071068, 0.0),
num_quaternion::Quaternion::new(-0.7071068, 0.0, 0.7071068, 0.0),
num_quaternion::Quaternion::new(0.0, 0.7071068, 0.0, -0.7071068),
num_quaternion::Quaternion::new(0.7071068, 0.7071068, 0.0, 0.0),
num_quaternion::Quaternion::new(0.0, 0.0, -0.7071068, 0.7071068),
num_quaternion::Quaternion::new(-0.7071068, 0.7071068, 0.0, 0.0),
num_quaternion::Quaternion::new(0.0, 0.0, -0.7071068, -0.7071068),
];

File diff suppressed because one or more lines are too long