Add persisted radar emission switch

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
2026-11-02 23:49:40 +03:00
parent 952a10f360
commit 26b258f00f
5 changed files with 25 additions and 7 deletions
+3 -1
View File
@@ -30,6 +30,7 @@ pub struct SamRec {
pub mode: sim::battery::SamMode,
pub radar_role: sim::battery::RadarRole,
pub spent_cells: u8,
pub radar_on: bool,
}
#[derive(Default)]
@@ -141,6 +142,7 @@ impl Layout {
};
let ground_request = l.num(28, "заявка RM48U", 0.0, 1.0)?.unwrap_or(0.0) == 1.0;
if ground_request && kind != SamKind::S400 { return Err(l.bad("заявка RM48U", "нужен С-400")); }
let radar_on = l.flag(29, "излучение РЛС")?.unwrap_or(true);
if radar_role != sim::battery::RadarRole::Multifunction && kind != SamKind::Radar {
return Err(l.bad("роль РЛС", "роль поддерживается только отдельной РЛС"));
}
@@ -153,7 +155,7 @@ impl Layout {
if let Some((m, _, _, _)) = ammo && m + ground_rounds > 4 && kind == SamKind::S400 {
return Err(l.bad("боезапас", "больше четырёх ячеек"));
}
out.sams.push(SamRec { kind, pos, heading, roles, health, cargo, ammo, dest, comps, ground_rounds, ground_stock, air_48n6_stock, mode, radar_role, spent_cells, ground_request });
out.sams.push(SamRec { kind, pos, heading, roles, health, cargo, ammo, dest, comps, ground_rounds, ground_stock, air_48n6_stock, mode, radar_role, spent_cells, ground_request, radar_on });
}
"target" => {
if out.targets.len() >= MAX_UNITS {
+13 -4
View File
@@ -897,7 +897,7 @@ impl AirDef {
let comps = if fresh { new.comps } else { s.comps };
let parts = comps.0.iter().map(|v| format!("{v:.3}")).collect::<Vec<_>>().join(" ");
text += &format!(
"sam {} {} {} {} {} {} {} {} {} {} {} {} {dest} {parts} {} {} {} {} {} {} {}\n",
"sam {} {} {} {} {} {} {} {} {} {} {} {} {dest} {parts} {} {} {} {} {} {} {} {}\n",
s.kind.key(),
s.pos.x,
s.pos.y,
@@ -910,7 +910,7 @@ impl AirDef {
bursts,
u8::from(empty),
reloading,
s.ground_rounds, s.ground_stock, s.air_48n6_stock, s.mode.key(), s.radar_role.key(), if fresh { 0 } else { s.spent_cells }, u8::from(!fresh && s.ground_request),
s.ground_rounds, s.ground_stock, s.air_48n6_stock, s.mode.key(), s.radar_role.key(), if fresh { 0 } else { s.spent_cells }, u8::from(!fresh && s.ground_request), s.radar_on,
);
}
for (i, t) in self.sim.targets.iter().enumerate() {
@@ -1025,6 +1025,7 @@ impl AirDef {
sam.fit_cells();
sam.set_mode(r.mode);
sam.set_radar_role(r.radar_role);
sam.radar_on = r.radar_on;
sam.spent_cells = r.spent_cells;
sam.sync_tube_cells();
self.sim.sams.push(sam);
@@ -1120,7 +1121,7 @@ fn durable_mark(sim: &Sim) -> u64 {
let ammo = u64::from(s.missiles) | u64::from(s.bursts) << 16 | u64::from(s.cargo) << 32 | u64::from(s.empty) << 48 | u64::from(s.reloading > 0) << 49;
let typed = u64::from(s.ground_rounds) ^ u64::from(s.ground_stock).rotate_left(17) ^ u64::from(s.air_48n6_stock).rotate_left(39) ^ u64::from(s.ground_request).rotate_left(55);
let parts = s.comps.0.iter().fold(0u64, |a, v| a.wrapping_mul(131).wrapping_add((v * 100.0) as u64));
((s.health * 1000.0) as u64).wrapping_mul(1_000_003) ^ place ^ ammo.rotate_left(7) ^ parts.rotate_left(13) ^ typed.rotate_left(29) ^ (s.mode as u64).rotate_left(3) ^ (s.radar_role as u64).rotate_left(11) ^ u64::from(s.spent_cells).rotate_left(19)
((s.health * 1000.0) as u64).wrapping_mul(1_000_003) ^ place ^ ammo.rotate_left(7) ^ parts.rotate_left(13) ^ typed.rotate_left(29) ^ (s.mode as u64).rotate_left(3) ^ (s.radar_role as u64).rotate_left(11) ^ u64::from(s.spent_cells).rotate_left(19) ^ u64::from(s.radar_on).rotate_left(23)
})
.chain(sim.target_health.iter().map(|h| (h * 10.0) as u64))
.fold(sim.sams.len() as u64, |a, h| a.wrapping_mul(31).wrapping_add(h))
@@ -2278,7 +2279,7 @@ impl Mod for AirDef {
["battery"] => {
let status = self.sim.battery_status();
for line in &status { say(line); }
Some("/airdef battery <номер> integrated|launcher; /airdef radar <номер> multi|search|engagement|height".into())
Some("/airdef battery <номер> integrated|launcher; /airdef radar <номер> on|off|multi|search|engagement|height".into())
}
["battery", n, mode] => {
let i = n.parse::<usize>().ok().and_then(|n| n.checked_sub(1)).ok_or("Нужен номер комплекса")?;
@@ -2288,6 +2289,14 @@ impl Mod for AirDef {
self.dirty = true;
Some(format!("№{} {}: режим {}", i + 1, sam.kind.name(), mode.key()))
}
["radar", n, state @ ("on" | "off")] => {
let i = n.parse::<usize>().ok().and_then(|n| n.checked_sub(1)).ok_or("Нужен номер комплекса")?;
let sam = self.sim.sams.get_mut(i).ok_or("Нет такого комплекса")?;
if sam.kind.spec().sensor <= 0.0 || sam.mode == sim::battery::SamMode::LauncherOnly { return Err("У комплекса нет активной РЛС".into()); }
sam.radar_on = *state == "on";
self.dirty = true;
Some(format!("№{} {}: излучение РЛС {}", i + 1, sam.kind.name(), state))
}
["radar", n, role] => {
let i = n.parse::<usize>().ok().and_then(|n| n.checked_sub(1)).ok_or("Нужен номер РЛС")?;
let role = sim::battery::RadarRole::from_key(role).ok_or("Роль: multi, search, engagement или height")?;
+3
View File
@@ -1364,6 +1364,8 @@ pub struct Sam {
pub kind: SamKind,
pub mode: battery::SamMode,
pub radar_role: battery::RadarRole,
/// Player-controlled active radar emission; independent of component health and data link.
pub radar_on: bool,
pub spent_cells: u8,
preparing: Option<launch::Prepare>,
cap_cell: u8,
@@ -1458,6 +1460,7 @@ impl Sam {
kind,
mode: battery::SamMode::Integrated,
radar_role: battery::RadarRole::Multifunction,
radar_on: true,
spent_cells: 0,
preparing: None,
cap_cell: 0,
+5 -1
View File
@@ -25,6 +25,7 @@ pub struct SamKey {
parts: [f32; 6],
mode: battery::SamMode,
radar_role: battery::RadarRole,
radar_on: bool,
spent_cells: u8,
cap_cell: u8,
cap_open: f32,
@@ -45,6 +46,7 @@ impl SamKey {
parts: sam.comps.0.map(|v| v as f32),
mode: sam.mode,
radar_role: sam.radar_role,
radar_on: sam.radar_on,
spent_cells: sam.spent_cells,
cap_cell: sam.cap_cell,
cap_open: sam.cap_open,
@@ -61,7 +63,7 @@ impl SamKey {
&& self.beam == o.beam
&& self.health == o.health
&& self.parts == o.parts
&& self.mode == o.mode && self.radar_role == o.radar_role
&& self.mode == o.mode && self.radar_role == o.radar_role && self.radar_on == o.radar_on
&& self.spent_cells == o.spent_cells && self.cap_cell == o.cap_cell
&& near(self.cap_open, o.cap_open, 0.001)
}
@@ -108,6 +110,7 @@ impl SamTrack {
parts: a.parts,
mode: a.mode,
radar_role: a.radar_role,
radar_on: a.radar_on,
spent_cells: a.spent_cells,
cap_cell: a.cap_cell,
cap_open: if a.cap_cell == b.cap_cell && a.spent_cells == b.spent_cells { l(a.cap_open, b.cap_open) } else { a.cap_open },
@@ -127,6 +130,7 @@ impl SamTrack {
sam.comps = Comps(k.parts.map(|v| v as f64));
sam.mode = k.mode;
sam.radar_role = k.radar_role;
sam.radar_on = k.radar_on;
sam.spent_cells = k.spent_cells;
sam.cap_cell = k.cap_cell;
sam.cap_open = k.cap_open;
+1 -1
View File
@@ -327,7 +327,7 @@ impl Sam {
}
/// Its own radar works: it has one, it is not failed and the unit is deployed.
pub fn radar_up(&self) -> bool {
self.mode != battery::SamMode::LauncherOnly && self.alive() && !self.moving() && self.kind.spec().sensor > 0.0 && self.part(Part::Sensor) > 0.0
self.radar_on && self.mode != battery::SamMode::LauncherOnly && self.alive() && !self.moving() && self.kind.spec().sensor > 0.0 && self.part(Part::Sensor) > 0.0
}
pub fn track(&self, id: u32) -> Option<&Track> {
self.tracks.iter().find(|t| t.id == id)