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:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -2425,18 +2425,21 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oj_factory_api"
|
||||
name = "oj_factory_web"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"actix-files",
|
||||
"actix-web",
|
||||
"base64",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"git-version",
|
||||
"handlebars",
|
||||
"libfj",
|
||||
"log",
|
||||
"oj_rc_core",
|
||||
"oj_rc_factory",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
|
||||
387
rc_core/src/cubes/connections.rs
Normal file
387
rc_core/src/cubes/connections.rs
Normal 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),
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
rc_core/src/cubes/rotations.rs
Normal file
27
rc_core/src/cubes/rotations.rs
Normal 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
@@ -5,6 +5,7 @@ pub struct GameMatches {
|
||||
map_configs: std::collections::HashMap<String, oj_rc_core::persist::config::MapConfig>,
|
||||
//fake_players: Vec<oj_rc_core::persist::config::FakePlayer>,
|
||||
cube_parsers: std::sync::Arc<oj_rc_core::cubes::CubeParsers>,
|
||||
ba_sorted_crystals: tokio::sync::RwLock<std::sync::Arc<Vec<oj_rc_core::cubes::CubeLocationInfo>>>, // cached after first calculation
|
||||
ba_settings: std::sync::Arc<oj_rc_core::persist::config::BattleArenaResolver>,
|
||||
pit_settings: std::sync::Arc<oj_rc_core::persist::config::PitSettings>,
|
||||
tdm_settings: std::sync::Arc<oj_rc_core::persist::config::TeamDeathMatchSettings>,
|
||||
@@ -23,6 +24,7 @@ impl GameMatches {
|
||||
.collect(),
|
||||
//fake_players: <oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::fake_players(conf),
|
||||
cube_parsers,
|
||||
ba_sorted_crystals: tokio::sync::RwLock::new(std::sync::Arc::new(Vec::default())),
|
||||
ba_settings: std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::ba_settings(conf)),
|
||||
pit_settings: std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::pit_settings(conf)),
|
||||
tdm_settings: std::sync::Arc::new(<oj_rc_core::persist::config::ConfigImpl as oj_rc_core::ConfigProvider<()>>::tdm_settings(conf)),
|
||||
@@ -110,7 +112,17 @@ impl GameMatches {
|
||||
code: oj_rc_core::persist::user::MultiplayerErrorCode::CustomString,
|
||||
message: e.error_msg().map(|x| x.to_owned()).unwrap_or_else(|| "Failed to resolve special settings for Battle Arena".to_owned()),
|
||||
})?;
|
||||
let inner = super::modes::BattleArenaLogic::new(&self.mode_configs.battle_arena, &map_config, &self.cube_parsers, &players, resolved_ba_conf);
|
||||
let crystals = if self.ba_sorted_crystals.read().await.is_empty() {
|
||||
let crystals = std::sync::Arc::new(
|
||||
self.cube_parsers.locations_of()
|
||||
.locations_of_reactor_sort(&mut std::io::Cursor::new(&resolved_ba_conf.base_machine_map))
|
||||
);
|
||||
*self.ba_sorted_crystals.write().await = crystals.clone();
|
||||
crystals
|
||||
} else {
|
||||
self.ba_sorted_crystals.read().await.clone()
|
||||
};
|
||||
let inner = super::modes::BattleArenaLogic::new(&self.mode_configs.battle_arena, &map_config, &players, resolved_ba_conf, crystals);
|
||||
let engine = super::GenericGamemodeEngine::new(
|
||||
game_info,
|
||||
map_config,
|
||||
|
||||
@@ -589,17 +589,17 @@ impl EqualizerTracker {
|
||||
}
|
||||
}
|
||||
|
||||
async fn damage_equalizer(&self, damage: i32, generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData, bases: &std::collections::HashMap<u8, BaseInfo>, crystals: &[oj_rc_core::cubes::CubeLocationInfo]) {
|
||||
async fn damage_equalizer(&self, damage: i32, generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData, bases: &std::collections::HashMap<u8, BaseInfo>, crystals: &[oj_rc_core::cubes::CubeLocationInfo], crystal_health: u32) {
|
||||
if !self.activated.load(std::sync::atomic::Ordering::Relaxed) { return; }
|
||||
let damage_u64 = damage as u64;
|
||||
let old_health = self.health.fetch_sub(damage as u64, std::sync::atomic::Ordering::Relaxed);
|
||||
if old_health <= damage_u64 {
|
||||
// equalizer is now destroyed
|
||||
self.destroy_equalizer(generic, ba_config, bases, crystals).await;
|
||||
self.destroy_equalizer(generic, ba_config, bases, crystals, crystal_health).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn destroy_equalizer(&self, generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData, bases: &std::collections::HashMap<u8, BaseInfo>, crystals: &[oj_rc_core::cubes::CubeLocationInfo]) {
|
||||
async fn destroy_equalizer(&self, generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData, bases: &std::collections::HashMap<u8, BaseInfo>, crystals: &[oj_rc_core::cubes::CubeLocationInfo], crystal_health: u32) {
|
||||
if !self.activated.swap(false, std::sync::atomic::Ordering::Relaxed) { return; }
|
||||
self.cancelled.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
let eq_start = self.start.load(std::sync::atomic::Ordering::Relaxed);
|
||||
@@ -615,7 +615,7 @@ impl EqualizerTracker {
|
||||
losing_base.cube_index.store(winning_base.cube_index.load(std::sync::atomic::Ordering::Relaxed), std::sync::atomic::Ordering::Relaxed);
|
||||
let new_index = losing_base.max_index();
|
||||
for i in old_index..new_index {
|
||||
losing_base.crystals_healths[i].store(u8::MAX, std::sync::atomic::Ordering::Relaxed);
|
||||
losing_base.crystals_healths[i].store(crystal_health, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
let base_heal = losing_base.generate_full_base_heal(losing_team, ba_config.protonium_health as u32, crystals);
|
||||
generic.broadcast(
|
||||
@@ -739,7 +739,7 @@ impl BaseTracker {
|
||||
//let base = self.bases.get(&base_id).unwrap();
|
||||
let target_crystals = &crystals[old_index..new_index];
|
||||
for crystal_i in old_index..new_index {
|
||||
base.crystals_healths[crystal_i].store(u8::MAX, std::sync::atomic::Ordering::Relaxed);
|
||||
base.crystals_healths[crystal_i].store(crystal_health, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
rlnl::events::HealedCubes {
|
||||
healed_machine: base_id as u16,
|
||||
@@ -748,9 +748,12 @@ impl BaseTracker {
|
||||
num_healed_cubes: target_crystals.len() as _,
|
||||
hit_cubes: target_crystals
|
||||
.iter()
|
||||
.map(|loc| rlnl::types::HitCubeInfo {
|
||||
pos: rlnl::types::Byte3 { x: loc.x, y: loc.y, z: loc.z, },
|
||||
damage: crystal_health as i32,
|
||||
.map(|loc| {
|
||||
log::trace!("Healing base cube at grid ({}, {}, {})", loc.x, loc.y, loc.z);
|
||||
rlnl::types::HitCubeInfo {
|
||||
pos: rlnl::types::Byte3 { x: loc.x, y: loc.y, z: loc.z, },
|
||||
damage: crystal_health as i32,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
@@ -787,21 +790,23 @@ impl BaseTracker {
|
||||
* ((PointTracker::TICK_MS as f32) / (generic.game_duration.as_millis() as f32))
|
||||
* ((self.bases.len() as f32) / (capture_points_count as f32));
|
||||
let increment = tick_info.delta as f32 * (*owned_points as f32) * one_tick * multiplier;
|
||||
#[cfg(debug_assertions)]
|
||||
let increment = increment + 0.1;
|
||||
let old_float_index = tracked_base.cube_index.fetch_add(increment, std::sync::atomic::Ordering::SeqCst);
|
||||
let new_float_index = old_float_index + increment;
|
||||
let old_index = (old_float_index.ceil() as usize).clamp(0, crystals.len());
|
||||
let new_index = (new_float_index.ceil() as usize).clamp(0, crystals.len());
|
||||
if new_index != old_index {
|
||||
log::debug!("Base {} increment passed a crystal index barrier", base_id);
|
||||
log::trace!("Base {} increment passed a crystal index barrier", base_id);
|
||||
let first_damaged = tracked_base.first_damaged(old_index, crystal_health);
|
||||
let payload = if new_index - old_index == 1 && first_damaged.is_some() {
|
||||
// undo cube_index update
|
||||
tracked_base.cube_index.fetch_sub(increment, std::sync::atomic::Ordering::SeqCst);
|
||||
log::debug!("Skipping increment in favour of healing damaged/destroyed cube");
|
||||
log::trace!("Skipping increment in favour of healing damaged/destroyed cube");
|
||||
#[allow(clippy::unnecessary_unwrap)] // have you seen the mess this would be with another if statement?
|
||||
let first_damaged = first_damaged.unwrap();
|
||||
let healing = crystal_health - tracked_base.calculate_crystal_health(first_damaged, crystal_health);
|
||||
tracked_base.crystals_healths[first_damaged].store(u8::MAX, std::sync::atomic::Ordering::Relaxed);
|
||||
//let healing = crystal_health - tracked_base.calculate_crystal_health(first_damaged, crystal_health);
|
||||
tracked_base.crystals_healths[first_damaged].store(crystal_health, std::sync::atomic::Ordering::Relaxed);
|
||||
let target_crystal = &crystals[first_damaged];
|
||||
rlnl::events::HealedCubes {
|
||||
healed_machine: *base_id as u16,
|
||||
@@ -811,7 +816,8 @@ impl BaseTracker {
|
||||
hit_cubes: vec![
|
||||
rlnl::types::HitCubeInfo {
|
||||
pos: rlnl::types::Byte3 { x: target_crystal.x, y: target_crystal.y, z: target_crystal.z, },
|
||||
damage: healing as i32,
|
||||
//damage: healing as i32,
|
||||
damage: crystal_health.saturating_sub(2) as i32,
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -843,6 +849,14 @@ impl BaseTracker {
|
||||
true
|
||||
).await;
|
||||
|
||||
let charge_ratio = (new_index as f64) / (crystals.len() as f64);
|
||||
let old_charge_ratio = (old_index as f64) / (crystals.len() as f64);
|
||||
let charge_percent = (charge_ratio * 100.0) as usize;
|
||||
let old_charge_percent = (old_charge_ratio * 100.0) as usize;
|
||||
if charge_percent != old_charge_percent && charge_percent.is_multiple_of(10) {
|
||||
log::debug!("Base {} is charged to {}% ({}/{}) in game {}", base_id, charge_percent, new_index, crystals.len(), generic.game_guid());
|
||||
}
|
||||
|
||||
if new_index == crystals.len() {
|
||||
// team base is charged to 100%
|
||||
//self.do_win(*base_id, WinMode::BaseFull, generic).await;
|
||||
@@ -862,7 +876,7 @@ impl BaseTracker {
|
||||
|
||||
struct BaseInfo {
|
||||
cube_index: atomic_float::AtomicF32,
|
||||
crystals_healths: Vec<std::sync::atomic::AtomicU8>,
|
||||
crystals_healths: Vec<std::sync::atomic::AtomicU32>,
|
||||
}
|
||||
|
||||
impl BaseInfo {
|
||||
@@ -870,15 +884,14 @@ impl BaseInfo {
|
||||
Self {
|
||||
cube_index: atomic_float::AtomicF32::new(0.0),
|
||||
crystals_healths: (0..crystals.len())
|
||||
.map(|_| std::sync::atomic::AtomicU8::new(0))
|
||||
.map(|_| std::sync::atomic::AtomicU32::new(0))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn calculate_crystal_health(&self, i: usize, max_health: u32) -> u32 {
|
||||
(((self.crystals_healths[i].load(std::sync::atomic::Ordering::Relaxed) as f32) / (u8::MAX as f32))
|
||||
* (max_health as f32)).ceil() as u32
|
||||
fn calculate_crystal_health(&self, i: usize) -> u32 {
|
||||
self.crystals_healths[i].load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -898,7 +911,7 @@ impl BaseInfo {
|
||||
|
||||
fn first_damaged(&self, old_index: usize, max_health: u32) -> Option<usize> {
|
||||
for i in 0..old_index {
|
||||
let health = self.calculate_crystal_health(i, max_health);
|
||||
let health = self.calculate_crystal_health(i);
|
||||
if health != 0 && health != max_health {
|
||||
return Some(i);
|
||||
}
|
||||
@@ -907,27 +920,27 @@ impl BaseInfo {
|
||||
}
|
||||
|
||||
/// returns whether crystal exists
|
||||
fn damage_crystal_at_pos(&self, pos: rlnl::types::Byte3, damage: i32, crystals: &[oj_rc_core::cubes::CubeLocationInfo], max_health: u32) -> bool {
|
||||
fn damage_crystal_at_pos(&self, pos: rlnl::types::Byte3, damage: i32, crystals: &[oj_rc_core::cubes::CubeLocationInfo]) -> Option<usize> {
|
||||
if let Some((index, _)) = crystals.iter().enumerate().find(|(_i, crystal)| crystal.x == pos.x && crystal.y == pos.y && crystal.z == pos.z) {
|
||||
let max_index = self.max_index();
|
||||
if index > max_index { return false; }
|
||||
self.damage_crystal(index, damage, max_health);
|
||||
true
|
||||
if index > max_index { return None; }
|
||||
self.damage_crystal(index, damage);
|
||||
Some(index)
|
||||
} else {
|
||||
false
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// returns whether crystal was destroyed
|
||||
fn damage_crystal(&self, i: usize, damage: i32, max_health: u32) -> bool {
|
||||
let damage_byte = (((damage as f32) / (max_health as f32)) * (u8::MAX as f32)).ceil() as u8;
|
||||
let old_health = self.crystals_healths[i].fetch_sub(damage_byte, std::sync::atomic::Ordering::Relaxed);
|
||||
log::debug!("Crystal {} damaged (was {}, now {}, delta {})", i, old_health, old_health.saturating_sub(damage_byte), damage_byte);
|
||||
if old_health < damage_byte {
|
||||
fn damage_crystal(&self, i: usize, damage: i32) -> bool {
|
||||
let damage = damage as u32;
|
||||
let old_health = self.crystals_healths[i].fetch_sub(damage, std::sync::atomic::Ordering::Relaxed);
|
||||
log::info!("Crystal {} damaged (was {}, now {}, delta {})", i, old_health, old_health.saturating_sub(damage), damage);
|
||||
if damage > old_health {
|
||||
// guarantee underflow behaviour
|
||||
self.crystals_healths[i].store(0, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
old_health != 0 && old_health >= damage_byte
|
||||
old_health != 0 && damage >= old_health
|
||||
}
|
||||
|
||||
/// returns whether crystal exists
|
||||
@@ -950,7 +963,7 @@ impl BaseInfo {
|
||||
let max_index = self.max_index();
|
||||
let target_crystals: Vec<rlnl::types::HitCubeInfo> = (0..crystals.len())
|
||||
.take_while(|&i| i <= max_index)
|
||||
.filter(|&i| self.calculate_crystal_health(i, max_health) != 0)
|
||||
.filter(|&i| self.calculate_crystal_health(i) != 0)
|
||||
.map(|i| {
|
||||
let target_loc = &crystals[i];
|
||||
rlnl::types::HitCubeInfo {
|
||||
@@ -985,18 +998,14 @@ pub struct BattleArenaLogic {
|
||||
base_tracking: BaseTracker,
|
||||
surrender_tracking: super::trackers::SurrenderGameTracker,
|
||||
//cube_parser: std::sync::Arc<oj_rc_core::cubes::CubeLocationsParser>,
|
||||
crystals: Vec<oj_rc_core::cubes::CubeLocationInfo>,
|
||||
crystals: std::sync::Arc<Vec<oj_rc_core::cubes::CubeLocationInfo>>,
|
||||
config: oj_rc_core::data::battle_arena_config::BattleArenaData,
|
||||
}
|
||||
|
||||
impl BattleArenaLogic {
|
||||
const CRYSTAL_ID: u32 = 3950293873;
|
||||
const CLASP_ID: u32 = 606866102;
|
||||
|
||||
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, map: &oj_rc_core::persist::config::MapConfig, parsers: &oj_rc_core::cubes::CubeParsers, players: &[oj_rc_core::persist::user::PlayerDescriptor], ba_config: oj_rc_core::data::battle_arena_config::BattleArenaData) -> Self {
|
||||
let cube_parser = parsers.locations_of();
|
||||
let crystals = cube_parser.locations_of_by_distance_to_first(&mut std::io::Cursor::new(&ba_config.base_machine_map), Self::CRYSTAL_ID, Self::CLASP_ID);
|
||||
//let crystals = cube_parser.locations_of_by_distance_from(&mut std::io::Cursor::new(&ba_config.base_machine_map), Self::CRYSTAL_ID, (44, 255, 46));
|
||||
pub fn new(config: &oj_rc_core::data::game_mode::GameModeConfig, map: &oj_rc_core::persist::config::MapConfig, players: &[oj_rc_core::persist::user::PlayerDescriptor], ba_config: oj_rc_core::data::battle_arena_config::BattleArenaData, crystals: std::sync::Arc<Vec<oj_rc_core::cubes::CubeLocationInfo>>) -> Self {
|
||||
//let min_y = crystals.iter().min_by_key(|x| x.y).unwrap();
|
||||
//log::warn!("First crystal is as ({}, {}, {})", min_y.x, min_y.y, min_y.z);
|
||||
Self {
|
||||
respawn_full_heal_duration: config.respawn_full_heal_duration,
|
||||
respawn_heal_duration: config.respawn_heal_duration,
|
||||
@@ -1170,50 +1179,38 @@ impl BattleArenaLogic {
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>, _actual_damage_data: impl FnOnce(Vec<rlnl::types::CubeState>) -> crate::matches::RlnlPacket) {
|
||||
async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>, actual_damage_data: impl FnOnce(Vec<rlnl::types::CubeState>) -> crate::matches::RlnlPacket) {
|
||||
let base_id = cube_damage.hit_machine_id as u8;
|
||||
let mut total_destroyed = 0;
|
||||
let mut valid_cubes = Vec::with_capacity(cube_damage.hit_cubes.len());
|
||||
let mut total_damaged = 0;
|
||||
let mut actual_cube_damage = Vec::with_capacity(cube_damage.hit_cubes.len());
|
||||
if let Some(base) = self.base_tracking.bases.get(&base_id) {
|
||||
for hit_cube in cube_damage.hit_cubes.iter() {
|
||||
if let Some(damage) = hit_cube.status.damage {
|
||||
if base.damage_crystal_at_pos(hit_cube.loc, damage, &self.crystals, self.config.protonium_health as u32) {
|
||||
valid_cubes.push(hit_cube.to_owned());
|
||||
if base.damage_crystal_at_pos(hit_cube.loc, damage, &self.crystals).is_some() {
|
||||
actual_cube_damage.push(hit_cube.to_owned());
|
||||
total_damaged += 1;
|
||||
} else {
|
||||
log::warn!("Could not damage cube with damage status");
|
||||
}
|
||||
} else if matches!(hit_cube.status.ty, rlnl::types::CubeHistoryEventType::Destroy) {
|
||||
if base.destroy_crystal_at_pos(hit_cube.loc, &self.crystals) {
|
||||
valid_cubes.push(hit_cube.to_owned());
|
||||
// TODO handle cube graph disconnects
|
||||
actual_cube_damage.push(hit_cube.to_owned());
|
||||
total_destroyed += 1;
|
||||
} else {
|
||||
log::warn!("Did not destroy cube with destroy status");
|
||||
log::warn!("Could not destroy cube with destroy status");
|
||||
}
|
||||
}
|
||||
}
|
||||
/*generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::DestroyCubeNoEffect,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&rlnl::events::ingame::DestroyCubeNoEffect {
|
||||
shooting_machine_id: cube_damage.shooting_machine_id,
|
||||
hit_machine_id: cube_damage.hit_machine_id,
|
||||
target_type: rlnl::types::TargetType::TeamBase,
|
||||
num_hits: valid_cubes.len() as _,
|
||||
hit_cubes: valid_cubes,
|
||||
},
|
||||
true,
|
||||
).await;*/
|
||||
//let actual_damage_data = actual_damage_data(valid_cubes);
|
||||
/*generic.broadcast(
|
||||
log::debug!("Destroyed {} ({} damaged) base cubes; {}/{} valid", total_destroyed, total_damaged, actual_cube_damage.len(), cube_damage.hit_cubes.len());
|
||||
let actual_damage_data = actual_damage_data(actual_cube_damage);
|
||||
generic.broadcast(
|
||||
actual_damage_data.event,
|
||||
actual_damage_data.property,
|
||||
actual_damage_data.data.as_ref(),
|
||||
true,
|
||||
).await;*/
|
||||
/*generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&base.generate_full_base_heal(base_id, self.config.protonium_health as u32, &self.crystals),
|
||||
true,
|
||||
).await;*/
|
||||
).await;
|
||||
}
|
||||
if total_destroyed != 0 {
|
||||
if let Some(team_id) = self.player_tracking.team(cube_damage.shooting_machine_id as u8).await {
|
||||
@@ -1232,12 +1229,6 @@ impl BattleArenaLogic {
|
||||
old_index,
|
||||
new_index,
|
||||
);
|
||||
/*generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||
literustlib::packet::Property::ReliableOrdered,
|
||||
&base.generate_full_base_heal(team_id, self.config.protonium_health as u32, &self.crystals),
|
||||
true,
|
||||
).await;*/
|
||||
|
||||
generic.broadcast(
|
||||
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||
@@ -1253,11 +1244,12 @@ impl BattleArenaLogic {
|
||||
}
|
||||
|
||||
async fn do_equalizer_damage(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>) {
|
||||
let crystal_health = self.config.protonium_health as u32;
|
||||
for hit_cube in cube_damage.hit_cubes.iter() {
|
||||
if let Some(damage) = hit_cube.status.damage {
|
||||
self.base_tracking.equalizer.damage_equalizer(damage, generic, &self.config, &self.base_tracking.bases, &self.crystals).await;
|
||||
self.base_tracking.equalizer.damage_equalizer(damage, generic, &self.config, &self.base_tracking.bases, &self.crystals, crystal_health).await;
|
||||
} else if matches!(hit_cube.status.ty, rlnl::types::CubeHistoryEventType::Destroy) {
|
||||
self.base_tracking.equalizer.destroy_equalizer(generic, &self.config, &self.base_tracking.bases, &self.crystals).await;
|
||||
self.base_tracking.equalizer.destroy_equalizer(generic, &self.config, &self.base_tracking.bases, &self.crystals, crystal_health).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1512,7 +1504,7 @@ impl CustomGameLogic for BattleArenaLogic {
|
||||
}
|
||||
};
|
||||
self.do_team_base_stealing(&pseudo, generic, actual_damage_data).await;
|
||||
true
|
||||
false
|
||||
},
|
||||
rlnl::types::TargetType::EqualizerCrystal => {
|
||||
self.do_equalizer_damage(&pseudo, generic).await;
|
||||
@@ -1543,8 +1535,8 @@ impl CustomGameLogic for BattleArenaLogic {
|
||||
data: Box::new(cube_dmg.to_owned()),
|
||||
}
|
||||
};
|
||||
self.do_team_base_stealing(cube_damage, generic, &actual_damage_data).await;
|
||||
true
|
||||
self.do_team_base_stealing(cube_damage, generic, actual_damage_data).await;
|
||||
false
|
||||
},
|
||||
rlnl::types::TargetType::EqualizerCrystal => {
|
||||
self.do_equalizer_damage(cube_damage, generic).await;
|
||||
|
||||
@@ -19,6 +19,7 @@ impl <C: Send + 'static> SimpleOperation<C> for BattleArenaConfigurer {
|
||||
|
||||
async fn handle(&self, params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||
let user_info = user.user()?;
|
||||
log::warn!("Retrieved ba config");
|
||||
let data = self.ba_conf.resolve_typed(user_info.as_ref().as_ref(), self.factory.as_ref(), &self.weapon_list, &self.cpu_counter).await?;
|
||||
let mut params = params.to_dict();
|
||||
params.insert(PARAM_KEY, data);
|
||||
|
||||
Reference in New Issue
Block a user