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

Rotate players and reactors to face center of map by default

This commit is contained in:
NG (Graham)
2026-07-09 22:08:30 -04:00
parent bab455dc00
commit 4c0184dad1
5 changed files with 65 additions and 9 deletions

View File

@@ -1450,6 +1450,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
}
async fn send_sync_events(connection: UserSender, _player_id: u8, players: Vec<std::sync::Arc<oj_rc_core::persist::user::PlayerDescriptor>>, extra_packets: Vec<super::RlnlPacket>, map: std::sync::Arc<oj_rc_core::persist::config::MapConfig>) -> std::io::Result<()> {
let map_center = super::modes::calculate_center(map.spawns.values().flat_map(|x| x.iter()));
let num_players = players.len() as u8;
let sender = connection.rlnl();
sender.send_empty(
@@ -1518,7 +1519,7 @@ impl <L: super::CustomGameLogic> GenericGamemodeEngine<L> {
&rlnl::events::sync::SpawnPoint {
pos: rlnl::types::PosQuatPair {
pos: rlnl::types::CompressedVec3::from((spawn.x, spawn.y, spawn.z)),
rot: rlnl::types::CompressedQuat { x: 0, y: 0, z: 0 },
rot: super::modes::looking_at_point(spawn, &map_center),
},
owner: player.player_id,
},

View File

@@ -1124,6 +1124,7 @@ pub struct BattleArenaLogic {
crystals: std::sync::Arc<Vec<oj_rc_core::cubes::CubeLocationInfo>>,
config: oj_rc_core::data::battle_arena_config::BattleArenaData,
can_drop_shields: bool,
map_center: oj_rc_core::persist::config::Point,
}
impl BattleArenaLogic {
@@ -1151,6 +1152,7 @@ impl BattleArenaLogic {
can_drop_shields: super_conf.descriptor.overrides.as_ref()
.map(|overrides| overrides.base_shields_go_down)
.unwrap_or(true),
map_center: super::calculate_center(super_conf.map.spawns.values().flat_map(|x| x.iter())),
}
}
@@ -1300,6 +1302,7 @@ impl BattleArenaLogic {
respawn_timestamp,
connections,
spawn_point,
Some(self.map_center.clone()),
player_id,
player_desc.machine.is_alive.clone(),
));
@@ -1458,12 +1461,34 @@ impl CustomGameLogic for BattleArenaLogic {
property: literustlib::packet::Property::ReliableOrdered,
data: Box::new(rlnl::events::sync::GetTeamBase {
base_1: rlnl::types::PosQuatPair {
pos: generic.map_config.bases.get(&0).map(|(s, _)| (s.center.x, s.center.y, s.center.z)).unwrap_or((0.0, 0.0, 0.0)).into(),
rot: (0.0, 0.0, 0.0, 0.0).into(),
pos: generic.map_config.bases.get(&0)
.map(|(s, _)| (s.center.x, s.center.y, s.center.z))
.unwrap_or((0.0, 0.0, 0.0)).into(),
rot: generic.map_config.bases.get(&0)
.map(|(s, _)| {
let loc = oj_rc_core::persist::config::Point {
x: s.center.x,
y: s.center.y,
z: s.center.z,
};
super::looking_at_point(&loc, &self.map_center)
})
.unwrap_or((0.0, 0.0, 0.0, 0.0).into()),
},
base_2: rlnl::types::PosQuatPair {
pos: generic.map_config.bases.get(&1).map(|(s, _)| (s.center.x, s.center.y, s.center.z)).unwrap_or((0.0, 0.0, 0.0)).into(),
rot: (0.0, 0.0, 0.0, 0.0).into(),
pos: generic.map_config.bases.get(&1)
.map(|(s, _)| (s.center.x, s.center.y, s.center.z))
.unwrap_or((0.0, 0.0, 0.0)).into(),
rot: generic.map_config.bases.get(&1)
.map(|(s, _)| {
let loc = oj_rc_core::persist::config::Point {
x: s.center.x,
y: s.center.y,
z: s.center.z,
};
super::looking_at_point(&loc, &self.map_center)
})
.unwrap_or((0.0, 0.0, 0.0, 0.0).into()),
},
protonium_cube_health: self.config.protonium_health as i32,
}),

View File

@@ -16,13 +16,14 @@ pub use team_death_match::TeamDeathMatchLogic;
pub(super) mod trackers;
async fn respawn_player_after(after: chrono::DateTime<chrono::Utc>, players: Vec<crate::matches::generic::UserSender>, spawn: oj_rc_core::persist::config::Point, player_id: u8, alive_flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
async fn respawn_player_after(after: chrono::DateTime<chrono::Utc>, players: Vec<crate::matches::generic::UserSender>, spawn: oj_rc_core::persist::config::Point, looking_at: Option<oj_rc_core::persist::config::Point>, player_id: u8, alive_flag: std::sync::Arc<std::sync::atomic::AtomicBool>) {
let sleep_dur = after.signed_duration_since(chrono::Utc::now()).to_std().expect("Respawn duration too long to sleep");
tokio::time::sleep(sleep_dur).await;
let spawn_payload = rlnl::events::sync::SpawnPoint {
pos: rlnl::types::PosQuatPair {
pos: rlnl::types::CompressedVec3::from((spawn.x, spawn.y, spawn.z)),
rot: rlnl::types::CompressedQuat { x: 0, y: 0, z: 0 },
rot: looking_at.map(|at| looking_at_point(&spawn, &at))
.unwrap_or(rlnl::types::CompressedQuat { x: 0, y: 0, z: 0 }),
},
owner: player_id,
};
@@ -38,3 +39,26 @@ async fn respawn_player_after(after: chrono::DateTime<chrono::Utc>, players: Vec
}
alive_flag.store(true, std::sync::atomic::Ordering::Relaxed);
}
#[inline]
pub fn looking_at_point(loc: &oj_rc_core::persist::config::Point, at: &oj_rc_core::persist::config::Point) -> rlnl::types::CompressedQuat<4> {
let unit_quat = num_quaternion::UnitQuaternion::from_two_vectors(&[0.0, 0.0, 1.0], &[at.x - loc.x, 0.0, at.z - loc.z]);
let actual_quat = unit_quat.as_quaternion();
rlnl::types::CompressedQuat::from((actual_quat.w, actual_quat.x, actual_quat.y, actual_quat.z))
}
pub fn calculate_center<'a>(points: impl Iterator<Item = &'a oj_rc_core::persist::config::Point>) -> oj_rc_core::persist::config::Point {
let mut tally = (0.0_f64, 0.0_f64, 0.0_f64);
let mut total = 0;
for point in points {
total += 1;
tally.0 += point.x as f64;
tally.1 += point.y as f64;
tally.2 += point.z as f64;
}
oj_rc_core::persist::config::Point {
x: (tally.0 / total as f64) as _,
y: (tally.1 / total as f64) as _,
z: (tally.2 / total as f64) as _,
}
}

View File

@@ -160,6 +160,7 @@ pub struct PitLogic {
settings: std::sync::Arc<oj_rc_core::persist::config::PitSettings>,
timer_task: tokio::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
initial_spawns: Vec<(u8, oj_rc_core::persist::config::Point)>, // (player_id, spawn_point)
map_center: oj_rc_core::persist::config::Point,
}
impl PitLogic {
@@ -172,6 +173,7 @@ impl PitLogic {
timer_task: tokio::sync::Mutex::new(None),
settings: pit_settings,
initial_spawns: Self::generate_first_spawns(map, players),
map_center: super::calculate_center(map.pit_spawns.iter()),
}
}
@@ -338,7 +340,7 @@ impl PitLogic {
data: Box::new(rlnl::events::sync::SpawnPoint {
pos: rlnl::types::PosQuatPair {
pos: rlnl::types::CompressedVec3::from((spawn_point.x, spawn_point.y, spawn_point.z)),
rot: rlnl::types::CompressedQuat { x: 0, y: 0, z: 0 },
rot: super::looking_at_point(spawn_point, &self.map_center),
},
owner: *player_id,
})
@@ -370,6 +372,7 @@ impl PitLogic {
respawn_timestamp,
connections,
spawn_point,
Some(self.map_center.clone()),
player_id,
player_desc.machine.is_alive.clone(),
));

View File

@@ -212,10 +212,11 @@ pub struct TeamDeathMatchLogic {
score_tracking: ScoreTracker,
player_tracking: PlayerTracker,
surrender_tracking: super::trackers::SurrenderGameTracker,
map_center: oj_rc_core::persist::config::Point,
}
impl TeamDeathMatchLogic {
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], tdm_settings: std::sync::Arc<oj_rc_core::persist::config::TeamDeathMatchSettings>) -> Self {
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], tdm_settings: std::sync::Arc<oj_rc_core::persist::config::TeamDeathMatchSettings>) -> Self {
let self_destruct_is_kill = tdm_settings.self_destruct_is_kill;
TeamDeathMatchLogic {
respawn_heal_duration: config.respawn_heal_duration,
@@ -226,6 +227,7 @@ impl TeamDeathMatchLogic {
score_tracking: ScoreTracker::new(players, self_destruct_is_kill),
player_tracking: PlayerTracker::new(players),
surrender_tracking: super::trackers::SurrenderGameTracker::new(),
map_center: super::calculate_center(map.spawns.values().flat_map(|x| x.iter())),
}
}
@@ -309,6 +311,7 @@ impl TeamDeathMatchLogic {
respawn_timestamp,
connections,
spawn_point,
Some(self.map_center.clone()),
player_id,
player_desc.machine.is_alive.clone(),
));