mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add equalizer support and partial fast-charge (dominating) support #32
This commit is contained in:
@@ -418,10 +418,16 @@ impl <C: Clone + Send> super::ConfigProvider<C> for CubeConfig {
|
|||||||
z: point.z,
|
z: point.z,
|
||||||
}
|
}
|
||||||
}, point.percent_per_second)).collect();
|
}, point.percent_per_second)).collect();
|
||||||
|
let equalizer = super::Point {
|
||||||
|
x: conf.equalizer.x,
|
||||||
|
y: conf.equalizer.y,
|
||||||
|
z: conf.equalizer.z,
|
||||||
|
};
|
||||||
let map_conf = super::MapConfig {
|
let map_conf = super::MapConfig {
|
||||||
spawns,
|
spawns,
|
||||||
bases,
|
bases,
|
||||||
capture_points,
|
capture_points,
|
||||||
|
equalizer,
|
||||||
};
|
};
|
||||||
(map.into_conf(), map_conf)
|
(map.into_conf(), map_conf)
|
||||||
}).collect()
|
}).collect()
|
||||||
|
|||||||
@@ -361,6 +361,7 @@ pub struct MapConfig {
|
|||||||
pub spawns: std::collections::HashMap<u8, Vec<Point>>, // team -> points
|
pub spawns: std::collections::HashMap<u8, Vec<Point>>, // team -> points
|
||||||
pub bases: std::collections::HashMap<u8, (Sphere, f32)>, // team -> (base, capture speed)
|
pub bases: std::collections::HashMap<u8, (Sphere, f32)>, // team -> (base, capture speed)
|
||||||
pub capture_points: Vec<(Sphere, f32)>, // (capture point, capture speed)
|
pub capture_points: Vec<(Sphere, f32)>, // (capture point, capture speed)
|
||||||
|
pub equalizer: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -404,9 +405,13 @@ impl BattleArenaResolver {
|
|||||||
base_machine_map: base_data.robot_map,
|
base_machine_map: base_data.robot_map,
|
||||||
equalizer_model: equalizer_data.robot_map,
|
equalizer_model: equalizer_data.robot_map,
|
||||||
equalizer_health: self.data.equalizer_health as i64,
|
equalizer_health: self.data.equalizer_health as i64,
|
||||||
equalizer_trigger_time_seconds: vec![10, 10, 10, 10, 10], // TODO
|
equalizer_trigger_time_seconds: if let Some(trigger_time) = self.data.equalizer_trigger_time_s {
|
||||||
|
vec![trigger_time; 5]
|
||||||
|
} else {
|
||||||
|
Vec::default()
|
||||||
|
},
|
||||||
equalizer_warning_seconds: self.data.equalizer_warning_s as i64, // TODO
|
equalizer_warning_seconds: self.data.equalizer_warning_s as i64, // TODO
|
||||||
equalizer_duration_seconds: vec![20, 20, 20, 20, 20], // TODO
|
equalizer_duration_seconds: vec![self.data.equalizer_duration_s; 5],
|
||||||
capture_time_seconds_per_player: vec![30, 20, 10, 5, 1], // TODO
|
capture_time_seconds_per_player: vec![30, 20, 10, 5, 1], // TODO
|
||||||
num_segments: self.data.num_segments as i32,
|
num_segments: self.data.num_segments as i32,
|
||||||
heal_escalation_time_seconds: 5, // Unused?
|
heal_escalation_time_seconds: 5, // Unused?
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub struct MapConfig {
|
|||||||
pub spawn_points: Vec<SpawnPoint>,
|
pub spawn_points: Vec<SpawnPoint>,
|
||||||
pub bases: Vec<CaptureBase>,
|
pub bases: Vec<CaptureBase>,
|
||||||
pub capture_points: Vec<CapturePoint>,
|
pub capture_points: Vec<CapturePoint>,
|
||||||
|
pub equalizer: Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
@@ -92,6 +93,33 @@ impl CapturePoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct Point {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const fn offset(mut self, x: f32, y: f32, z: f32) -> Self {
|
||||||
|
self.x += x;
|
||||||
|
self.y += y;
|
||||||
|
self.z += z;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn rotated(mut self, rot: num_quaternion::Quaternion<f32>) -> Self {
|
||||||
|
let unit_rot = rot.normalize().expect("Bad rotation quaternion for Point");
|
||||||
|
let rotated = unit_rot.rotate_vector([self.x, self.y, self.z]);
|
||||||
|
self.x = rotated[0];
|
||||||
|
self.y = rotated[1];
|
||||||
|
self.z = rotated[2];
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_BASE_PERCENT_PER_SECOND: f32 = 2.5;
|
const DEFAULT_BASE_PERCENT_PER_SECOND: f32 = 2.5;
|
||||||
const DEFAULT_BASE_RADIUS: f32 = 20.0;
|
const DEFAULT_BASE_RADIUS: f32 = 20.0;
|
||||||
const DEFAULT_CAPTURE_PERCENT_PER_SECOND: f32 = DEFAULT_BASE_PERCENT_PER_SECOND * 1.5;
|
const DEFAULT_CAPTURE_PERCENT_PER_SECOND: f32 = DEFAULT_BASE_PERCENT_PER_SECOND * 1.5;
|
||||||
@@ -273,6 +301,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
y: 0.707107,
|
y: 0.707107,
|
||||||
z: 0.0,
|
z: 0.0,
|
||||||
}).offset(13.320, 0.0, -9.84)).collect(),
|
}).offset(13.320, 0.0, -9.84)).collect(),
|
||||||
|
equalizer: Point {
|
||||||
|
x: 1.404,
|
||||||
|
y: 14.3,
|
||||||
|
z: -0.588,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Earth2, MapConfig { // level4
|
map.insert(super::combat::GameMap::Earth2, MapConfig { // level4
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -483,6 +516,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Mars1, MapConfig {
|
map.insert(super::combat::GameMap::Mars1, MapConfig {
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -628,6 +666,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Mars2, MapConfig {
|
map.insert(super::combat::GameMap::Mars2, MapConfig {
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -773,6 +816,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
].into_iter().map(|x| x.offset(-434.640, 0.0, -414.720)).collect(),
|
].into_iter().map(|x| x.offset(-434.640, 0.0, -414.720)).collect(),
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Mars3, MapConfig {
|
map.insert(super::combat::GameMap::Mars3, MapConfig {
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -918,6 +966,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
].into_iter().map(|x| x.offset(49.608, 0.0, 52.493)).collect(),
|
].into_iter().map(|x| x.offset(49.608, 0.0, 52.493)).collect(),
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Neptune1, MapConfig {
|
map.insert(super::combat::GameMap::Neptune1, MapConfig {
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -1063,6 +1116,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
].into_iter().map(|x| x.offset(405.542, 0.0, 10.668)).collect(),
|
].into_iter().map(|x| x.offset(405.542, 0.0, 10.668)).collect(),
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Neptune2, MapConfig {
|
map.insert(super::combat::GameMap::Neptune2, MapConfig {
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -1208,6 +1266,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map.insert(super::combat::GameMap::Neptune3, MapConfig {
|
map.insert(super::combat::GameMap::Neptune3, MapConfig {
|
||||||
spawn_points: vec![
|
spawn_points: vec![
|
||||||
@@ -1353,6 +1416,11 @@ pub(super) fn default_map() -> std::collections::HashMap<super::combat::GameMap,
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
capture_points: vec![], // TODO
|
capture_points: vec![], // TODO
|
||||||
|
equalizer: Point { // TODO
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ pub struct BattleArenaConfig {
|
|||||||
pub equalizer: super::garage::PrefabVehicle,
|
pub equalizer: super::garage::PrefabVehicle,
|
||||||
#[serde(default = "default_equalizer_health")]
|
#[serde(default = "default_equalizer_health")]
|
||||||
pub equalizer_health: u64,
|
pub equalizer_health: u64,
|
||||||
//pub equalizer_trigger_time_s: Vec<u64>,
|
pub equalizer_trigger_time_s: Option<u64>,
|
||||||
#[serde(default = "default_equalizer_warning")]
|
#[serde(default = "default_equalizer_warning")]
|
||||||
pub equalizer_warning_s: u64,
|
pub equalizer_warning_s: u64,
|
||||||
#[serde(default = "default_equalizer_duration")]
|
#[serde(default = "default_equalizer_duration")]
|
||||||
@@ -114,6 +114,7 @@ pub(super) fn default_ba_conf() -> BattleArenaConfig {
|
|||||||
respawn_time_s: default_respawn_time(),
|
respawn_time_s: default_respawn_time(),
|
||||||
equalizer: default_equalizer(),
|
equalizer: default_equalizer(),
|
||||||
equalizer_health: default_equalizer_health(),
|
equalizer_health: default_equalizer_health(),
|
||||||
|
equalizer_trigger_time_s: None,
|
||||||
equalizer_warning_s: default_equalizer_warning(),
|
equalizer_warning_s: default_equalizer_warning(),
|
||||||
equalizer_duration_s: default_equalizer_duration(),
|
equalizer_duration_s: default_equalizer_duration(),
|
||||||
base: default_ba_base(),
|
base: default_ba_base(),
|
||||||
@@ -154,7 +155,7 @@ fn default_respawn_time() -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_equalizer_health() -> u64 {
|
fn default_equalizer_health() -> u64 {
|
||||||
1_000
|
1_000_000
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_equalizer_warning() -> u64 {
|
fn default_equalizer_warning() -> u64 {
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ impl GameMatches {
|
|||||||
spawns: std::collections::HashMap::default(),
|
spawns: std::collections::HashMap::default(),
|
||||||
bases: std::collections::HashMap::default(),
|
bases: std::collections::HashMap::default(),
|
||||||
capture_points: Vec::default(),
|
capture_points: Vec::default(),
|
||||||
|
equalizer: oj_rc_core::persist::config::Point {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let players = user.game_players(guid).await?;
|
let players = user.game_players(guid).await?;
|
||||||
|
|||||||
@@ -178,22 +178,28 @@ impl PointInfo {
|
|||||||
struct PointTracker {
|
struct PointTracker {
|
||||||
points: Vec<PointInfo>,
|
points: Vec<PointInfo>,
|
||||||
ticker: super::trackers::TickTracker<{Self::TICK_MS}>,
|
ticker: super::trackers::TickTracker<{Self::TICK_MS}>,
|
||||||
|
last_capture_team: std::sync::atomic::AtomicU8,
|
||||||
|
last_capture_time: std::sync::atomic::AtomicI64,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PointTickInfo {
|
struct PointTickInfo {
|
||||||
owned: std::collections::HashMap<u8, u8>, // team -> capture point count
|
owned: std::collections::HashMap<u8, u8>, // team -> capture point count
|
||||||
captured_firsts: std::collections::HashSet<u8>, // team
|
captured_firsts: std::collections::HashSet<u8>, // team
|
||||||
lost_lasts: std::collections::HashSet<u8>, // team
|
lost_lasts: std::collections::HashSet<u8>, // team
|
||||||
|
dominating: Option<u8>,
|
||||||
delta: u16,
|
delta: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointTracker {
|
impl PointTracker {
|
||||||
const TICK_MS: i64 = 50;
|
const TICK_MS: i64 = 50;
|
||||||
|
const TIME_BEFORE_DOMINANT_S: i64 = 30;
|
||||||
|
|
||||||
fn new(points: impl Iterator<Item=f32>) -> Self {
|
fn new(points: impl Iterator<Item=f32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
points: points.map(PointInfo::new).collect(),
|
points: points.map(PointInfo::new).collect(),
|
||||||
ticker: super::trackers::TickTracker::new(),
|
ticker: super::trackers::TickTracker::new(),
|
||||||
|
last_capture_team: std::sync::atomic::AtomicU8::new(u8::MAX),
|
||||||
|
last_capture_time: std::sync::atomic::AtomicI64::new(i64::MIN),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,6 +362,8 @@ impl PointTracker {
|
|||||||
log::info!("Point {} was captured by team {} in game {}", i, new_team, generic.game_guid());
|
log::info!("Point {} was captured by team {} in game {}", i, new_team, generic.game_guid());
|
||||||
cap_point.capture.store(0.0, std::sync::atomic::Ordering::SeqCst);
|
cap_point.capture.store(0.0, std::sync::atomic::Ordering::SeqCst);
|
||||||
cap_point.team.store(new_team, std::sync::atomic::Ordering::SeqCst);
|
cap_point.team.store(new_team, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
self.last_capture_team.store(stealing_team, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.last_capture_time.store(chrono::Utc::now().timestamp(), std::sync::atomic::Ordering::Relaxed);
|
||||||
if owned_points.get(&(new_team as u8)).copied().unwrap_or(0) == 0 {
|
if owned_points.get(&(new_team as u8)).copied().unwrap_or(0) == 0 {
|
||||||
captured_firsts.insert(new_team as u8);
|
captured_firsts.insert(new_team as u8);
|
||||||
}
|
}
|
||||||
@@ -387,17 +395,251 @@ impl PointTracker {
|
|||||||
true
|
true
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
|
let last_capture_team = self.last_capture_team.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let dominating = if last_capture_team != u8::MAX && self.points.iter().all(|p| p.team.load(std::sync::atomic::Ordering::SeqCst) == (last_capture_team as i8)) {
|
||||||
|
let last_capture_time = self.last_capture_time.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
if now > last_capture_time && now - last_capture_time >= Self::TIME_BEFORE_DOMINANT_S {
|
||||||
|
Some(last_capture_team)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
Some(PointTickInfo {
|
Some(PointTickInfo {
|
||||||
owned: owned_points,
|
owned: owned_points,
|
||||||
captured_firsts,
|
captured_firsts,
|
||||||
lost_lasts,
|
lost_lasts,
|
||||||
|
dominating,
|
||||||
delta,
|
delta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct EqualizerTracker {
|
||||||
|
is_disabled: bool,
|
||||||
|
start: std::sync::atomic::AtomicI64,
|
||||||
|
activated: std::sync::atomic::AtomicBool,
|
||||||
|
cancelled: std::sync::atomic::AtomicBool,
|
||||||
|
losing_team: std::sync::atomic::AtomicU8,
|
||||||
|
winning_team: std::sync::atomic::AtomicU8,
|
||||||
|
trigger_index: std::sync::atomic::AtomicU8,
|
||||||
|
health: std::sync::atomic::AtomicU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EqualizerTracker {
|
||||||
|
fn new(ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData) -> Self {
|
||||||
|
let is_disabled = ba_config.equalizer_health <= 0
|
||||||
|
|| ba_config.equalizer_model.is_empty()
|
||||||
|
//|| ba_config.equalizer_trigger_time_seconds.is_empty()
|
||||||
|
|| ba_config.equalizer_duration_seconds.is_empty()
|
||||||
|
|| ba_config.equalizer_duration_seconds.iter().any(|&x| x == 0);
|
||||||
|
if is_disabled {
|
||||||
|
log::info!("Battle Arena equalizer is disabled by config (model ok? {}, health ok? {}, duration ok? {})",
|
||||||
|
!ba_config.equalizer_model.is_empty(),
|
||||||
|
ba_config.equalizer_health > 0,
|
||||||
|
!ba_config.equalizer_duration_seconds.is_empty() && !ba_config.equalizer_duration_seconds.iter().any(|&x| x == 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
is_disabled,
|
||||||
|
start: std::sync::atomic::AtomicI64::new(i64::MIN),
|
||||||
|
activated: std::sync::atomic::AtomicBool::new(false),
|
||||||
|
cancelled: std::sync::atomic::AtomicBool::new(false),
|
||||||
|
losing_team: std::sync::atomic::AtomicU8::new(u8::MAX),
|
||||||
|
winning_team: std::sync::atomic::AtomicU8::new(u8::MAX),
|
||||||
|
trigger_index: std::sync::atomic::AtomicU8::new(0),
|
||||||
|
health: std::sync::atomic::AtomicU64::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tick(&self, generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, bases: &std::collections::HashMap<u8, BaseInfo>, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData) {
|
||||||
|
if self.is_disabled { return; }
|
||||||
|
let trigger_index = self.trigger_index.load(std::sync::atomic::Ordering::Relaxed) as usize;
|
||||||
|
let game_start = generic.game_start.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if game_start == i64::MIN { return; }
|
||||||
|
let mut eq_start = self.start.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if eq_start == i64::MIN {
|
||||||
|
eq_start = if let Some(trigger_time_s) = ba_config.equalizer_trigger_time_seconds.get(trigger_index) {
|
||||||
|
game_start + (*trigger_time_s as i64)
|
||||||
|
} else {
|
||||||
|
game_start + ((generic.game_duration.as_secs() / 2) as i64)
|
||||||
|
};
|
||||||
|
self.start.store(eq_start, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
let eq_start = eq_start; // no longer mutable
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
if now >= eq_start {
|
||||||
|
let eq_end = if let Some(duration_s) = ba_config.equalizer_duration_seconds.get(trigger_index) {
|
||||||
|
eq_start + (*duration_s as i64)
|
||||||
|
} else {
|
||||||
|
i64::MAX
|
||||||
|
};
|
||||||
|
let is_activated = self.activated.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let is_cancelled = self.cancelled.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if is_activated {
|
||||||
|
if now > eq_end {
|
||||||
|
// do deactivation
|
||||||
|
log::info!("Equalizer deactivated because the timer ran out");
|
||||||
|
self.activated.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.send_notification(rlnl::types::EqualizerState::Defended, ba_config, eq_start, now, generic).await;
|
||||||
|
} else {
|
||||||
|
// check for change in leading team
|
||||||
|
let old_winning_team = self.winning_team.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if let Some((winning_base_id, _)) = Self::winning_base(bases) {
|
||||||
|
if winning_base_id != old_winning_team {
|
||||||
|
// cancel equalizer
|
||||||
|
log::info!("Equalizer cancelled because winning base lost the lead");
|
||||||
|
self.cancelled.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.activated.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.send_notification(rlnl::types::EqualizerState::Lost, ba_config, eq_start, now, generic).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if now < eq_end && !is_cancelled {
|
||||||
|
// do activation
|
||||||
|
if let Some(winning_base) = Self::winning_base(bases) {
|
||||||
|
if let Some(losing_base) = Self::losing_base(bases) {
|
||||||
|
self.activated.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.winning_team.store(winning_base.0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.losing_team.store(losing_base.0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.health.store(ba_config.equalizer_health as u64, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.send_notification(rlnl::types::EqualizerState::Start, ba_config, eq_start, now, generic).await;
|
||||||
|
} else {
|
||||||
|
log::warn!("No losing team found for game {}", generic.game_guid());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("No winning team found for game {}", generic.game_guid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]) {
|
||||||
|
self.cancelled.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
self.activated.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let eq_start = self.start.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
self.send_notification(rlnl::types::EqualizerState::Destroyed, ba_config, eq_start, now, generic).await;
|
||||||
|
self.health.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
// give health to losing team
|
||||||
|
let winning_team = self.winning_team.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let losing_team = self.losing_team.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
if let Some(losing_base) = bases.get(&losing_team) {
|
||||||
|
if let Some(winning_base) = bases.get(&winning_team) {
|
||||||
|
let old_index = losing_base.max_index();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
let base_heal = losing_base.generate_full_base_heal(losing_team, ba_config.protonium_health as u32, crystals);
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&base_heal,
|
||||||
|
true
|
||||||
|
).await;
|
||||||
|
} else {
|
||||||
|
log::warn!("Winning base {} no longer exists for game {}", winning_team, generic.game_guid());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("Losing base {} no longer exists for game {}", losing_team, generic.game_guid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_notification(&self, variant: rlnl::types::EqualizerState, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData, start: i64, now: i64, generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>) {
|
||||||
|
if let Some(notif) = self.generate_notification(variant, ba_config, start, now) {
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::EqualizerNotification,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
¬if,
|
||||||
|
true,
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_notification(&self, variant: rlnl::types::EqualizerState, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData, start: i64, now: i64) -> Option<rlnl::events::sync::EqualizerNotification> {
|
||||||
|
let trigger_index = self.trigger_index.load(std::sync::atomic::Ordering::Relaxed) as usize;
|
||||||
|
if let Some(duration_s) = ba_config.equalizer_duration_seconds.get(trigger_index) {
|
||||||
|
let end = start + (*duration_s as i64);
|
||||||
|
//let losing_team = self.losing_team.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let winning_team = self.winning_team.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let current_health = self.health.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
let time_remaining = match variant {
|
||||||
|
rlnl::types::EqualizerState::Start => *duration_s as i16,
|
||||||
|
rlnl::types::EqualizerState::Lost
|
||||||
|
| rlnl::types::EqualizerState::Destroyed => {
|
||||||
|
if now >= start {
|
||||||
|
if now < end {
|
||||||
|
((*duration_s as i64) - (now - start)) as i16
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*duration_s as i16
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rlnl::types::EqualizerState::Defended => 0,
|
||||||
|
};
|
||||||
|
Some(rlnl::events::sync::EqualizerNotification {
|
||||||
|
notification: variant,
|
||||||
|
//team_id: losing_team as i16,
|
||||||
|
team_id: winning_team as i16,
|
||||||
|
time: time_remaining,
|
||||||
|
max_health: ba_config.equalizer_health as i32,
|
||||||
|
health: current_health as i32,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn winning_base(bases: &std::collections::HashMap<u8, BaseInfo>) -> Option<(u8, f32)> {
|
||||||
|
let mut max = None;
|
||||||
|
for (id, info) in bases.iter() {
|
||||||
|
if let Some((_, charge)) = max {
|
||||||
|
let new_charge = info.base_charge();
|
||||||
|
if new_charge > charge {
|
||||||
|
max = Some((*id, new_charge));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
max = Some((*id, info.base_charge()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max
|
||||||
|
}
|
||||||
|
|
||||||
|
fn losing_base(bases: &std::collections::HashMap<u8, BaseInfo>) -> Option<(u8, f32)> {
|
||||||
|
let mut min = None;
|
||||||
|
for (id, info) in bases.iter() {
|
||||||
|
if let Some((_, charge)) = min {
|
||||||
|
let new_charge = info.base_charge();
|
||||||
|
if new_charge < charge {
|
||||||
|
min = Some((*id, new_charge));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
min = Some((*id, info.base_charge()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
min
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct BaseTracker {
|
struct BaseTracker {
|
||||||
bases: std::collections::HashMap<u8, BaseInfo>,
|
bases: std::collections::HashMap<u8, BaseInfo>,
|
||||||
|
equalizer: EqualizerTracker,
|
||||||
|
dominating: std::sync::atomic::AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BaseTickInfo {
|
struct BaseTickInfo {
|
||||||
@@ -405,24 +647,51 @@ struct BaseTickInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BaseTracker {
|
impl BaseTracker {
|
||||||
fn new<'a>(bases_iter: impl std::iter::Iterator<Item=&'a u8>, crystals: &[oj_rc_core::cubes::CubeLocationInfo]) -> Self {
|
const DOMINATING_MULT: f32 = 10.0;
|
||||||
|
|
||||||
|
fn new<'a>(bases_iter: impl std::iter::Iterator<Item=&'a u8>, crystals: &[oj_rc_core::cubes::CubeLocationInfo], ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData) -> Self {
|
||||||
let mut bases = std::collections::HashMap::new();
|
let mut bases = std::collections::HashMap::new();
|
||||||
for base_id in bases_iter {
|
for base_id in bases_iter {
|
||||||
bases.insert(*base_id, BaseInfo::new(crystals));
|
bases.insert(*base_id, BaseInfo::new(crystals));
|
||||||
}
|
}
|
||||||
Self {
|
Self {
|
||||||
bases,
|
bases,
|
||||||
|
equalizer: EqualizerTracker::new(ba_config),
|
||||||
|
dominating: std::sync::atomic::AtomicBool::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick(&self, tick_info: &PointTickInfo, crystals: &[oj_rc_core::cubes::CubeLocationInfo], generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, capture_points_count: usize, crystal_health: u32) -> BaseTickInfo {
|
async fn tick(&self, tick_info: &PointTickInfo, crystals: &[oj_rc_core::cubes::CubeLocationInfo], generic: &crate::matches::GenericGamemodeEngine<BattleArenaLogic>, capture_points_count: usize, crystal_health: u32, ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData) -> BaseTickInfo {
|
||||||
|
let multiplier = if let Some(dominant_team) = tick_info.dominating {
|
||||||
|
if !self.dominating.swap(true, std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
log::info!("Team {} is now dominating game {}", dominant_team, generic.game_guid());
|
||||||
|
// FIXME this doesn't seem to trigger the announcement client-side
|
||||||
|
generic.broadcast(
|
||||||
|
rlnl::event_code::NetworkEvent::CapturePointNotification,
|
||||||
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
|
&rlnl::events::ingame::CapturePointNotification {
|
||||||
|
notification: rlnl::types::CapturePointNotificationType::Dominating,
|
||||||
|
id: dominant_team,
|
||||||
|
defending_team: dominant_team as i8,
|
||||||
|
attacking_team: (((dominant_team as usize) + 1) % generic.map_config.bases.len()) as i8
|
||||||
|
},
|
||||||
|
true
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
Self::DOMINATING_MULT
|
||||||
|
} else {
|
||||||
|
if self.dominating.swap(false, std::sync::atomic::Ordering::Relaxed) {
|
||||||
|
log::info!("No longer dominating game {}", generic.game_guid());
|
||||||
|
}
|
||||||
|
1.0
|
||||||
|
};
|
||||||
for (base_id, tracked_base) in self.bases.iter() {
|
for (base_id, tracked_base) in self.bases.iter() {
|
||||||
//log::info!("Healing base {}", base_id);
|
//log::info!("Healing base {}", base_id);
|
||||||
if let Some(owned_points) = tick_info.owned.get(base_id) {
|
if let Some(owned_points) = tick_info.owned.get(base_id) {
|
||||||
let one_tick = (crystals.len() as f32)
|
let one_tick = (crystals.len() as f32)
|
||||||
* ((PointTracker::TICK_MS as f32) / (generic.game_duration.as_millis() as f32))
|
* ((PointTracker::TICK_MS as f32) / (generic.game_duration.as_millis() as f32))
|
||||||
* ((self.bases.len() as f32) / (capture_points_count 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;
|
let increment = tick_info.delta as f32 * (*owned_points as f32) * one_tick * multiplier;
|
||||||
let old_float_index = tracked_base.cube_index.fetch_add(increment, std::sync::atomic::Ordering::SeqCst);
|
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 new_float_index = old_float_index + increment;
|
||||||
let old_index = (old_float_index.ceil() as usize).clamp(0, crystals.len());
|
let old_index = (old_float_index.ceil() as usize).clamp(0, crystals.len());
|
||||||
@@ -487,6 +756,7 @@ impl BaseTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.equalizer.tick(generic, &self.bases, ba_config).await;
|
||||||
BaseTickInfo {
|
BaseTickInfo {
|
||||||
win: None,
|
win: None,
|
||||||
}
|
}
|
||||||
@@ -519,6 +789,16 @@ impl BaseInfo {
|
|||||||
self.cube_index.load(std::sync::atomic::Ordering::SeqCst).ceil() as usize
|
self.cube_index.load(std::sync::atomic::Ordering::SeqCst).ceil() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// total base health, out of 1
|
||||||
|
fn base_charge(&self) -> f32 {
|
||||||
|
let mut total_health: usize = 0;
|
||||||
|
for crystal in self.crystals_healths.iter() {
|
||||||
|
let health = crystal.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
total_health += health as usize;
|
||||||
|
}
|
||||||
|
(total_health as f32) / ((self.crystals_healths.len() * u8::MAX as usize) as f32)
|
||||||
|
}
|
||||||
|
|
||||||
fn first_damaged(&self, old_index: usize, max_health: u32) -> Option<usize> {
|
fn first_damaged(&self, old_index: usize, max_health: u32) -> Option<usize> {
|
||||||
for i in 0..old_index {
|
for i in 0..old_index {
|
||||||
let health = self.calculate_crystal_health(i, max_health);
|
let health = self.calculate_crystal_health(i, max_health);
|
||||||
@@ -630,7 +910,7 @@ impl BattleArenaLogic {
|
|||||||
player_tracking: PlayerTracker::new(),
|
player_tracking: PlayerTracker::new(),
|
||||||
capture_tracking: PointTracker::new(map.capture_points.iter().map(|(_, speed)| *speed)),
|
capture_tracking: PointTracker::new(map.capture_points.iter().map(|(_, speed)| *speed)),
|
||||||
surrender_tracking: super::trackers::SurrenderGameTracker::new(),
|
surrender_tracking: super::trackers::SurrenderGameTracker::new(),
|
||||||
base_tracking: BaseTracker::new(map.bases.keys(), &crystals),
|
base_tracking: BaseTracker::new(map.bases.keys(), &crystals, &ba_config),
|
||||||
//cube_parser,
|
//cube_parser,
|
||||||
//ba_base: teambase,
|
//ba_base: teambase,
|
||||||
//ba_equalizer: equalizer,
|
//ba_equalizer: equalizer,
|
||||||
@@ -810,7 +1090,7 @@ impl BattleArenaLogic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>) {
|
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 base_id = cube_damage.hit_machine_id as u8;
|
||||||
let mut total_destroyed = 0;
|
let mut total_destroyed = 0;
|
||||||
let mut valid_cubes = Vec::with_capacity(cube_damage.hit_cubes.len());
|
let mut valid_cubes = Vec::with_capacity(cube_damage.hit_cubes.len());
|
||||||
@@ -829,7 +1109,7 @@ impl BattleArenaLogic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generic.broadcast(
|
/*generic.broadcast(
|
||||||
rlnl::event_code::NetworkEvent::DestroyCubeNoEffect,
|
rlnl::event_code::NetworkEvent::DestroyCubeNoEffect,
|
||||||
literustlib::packet::Property::ReliableOrdered,
|
literustlib::packet::Property::ReliableOrdered,
|
||||||
&rlnl::events::ingame::DestroyCubeNoEffect {
|
&rlnl::events::ingame::DestroyCubeNoEffect {
|
||||||
@@ -840,6 +1120,13 @@ impl BattleArenaLogic {
|
|||||||
hit_cubes: valid_cubes,
|
hit_cubes: valid_cubes,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
).await;*/
|
||||||
|
let actual_damage_data = actual_damage_data(valid_cubes);
|
||||||
|
generic.broadcast(
|
||||||
|
actual_damage_data.event,
|
||||||
|
actual_damage_data.property,
|
||||||
|
actual_damage_data.data.as_ref(),
|
||||||
|
true,
|
||||||
).await;
|
).await;
|
||||||
generic.broadcast(
|
generic.broadcast(
|
||||||
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
rlnl::event_code::NetworkEvent::SyncTeamBaseCubes,
|
||||||
@@ -865,6 +1152,16 @@ impl BattleArenaLogic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn do_equalizer_damage(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine<Self>) {
|
||||||
|
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;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -949,10 +1246,10 @@ impl CustomGameLogic for BattleArenaLogic {
|
|||||||
property: literustlib::packet::Property::ReliableOrdered,
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
data: Box::new(rlnl::events::sync::GetEqualizer {
|
data: Box::new(rlnl::events::sync::GetEqualizer {
|
||||||
pos: rlnl::types::PosQuatPair {
|
pos: rlnl::types::PosQuatPair {
|
||||||
pos: (0.0, 0.0, 0.0).into(),
|
pos: (generic.map_config.equalizer.x, generic.map_config.equalizer.y, generic.map_config.equalizer.z).into(),
|
||||||
rot: (0.0, 0.0, 0.0, 0.0).into(),
|
rot: (0.0, 0.0, 0.0, 0.0).into(),
|
||||||
},
|
},
|
||||||
total_health: 42,
|
total_health: self.config.equalizer_health as i32,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
// SetShieldState
|
// SetShieldState
|
||||||
@@ -1086,15 +1383,71 @@ impl CustomGameLogic for BattleArenaLogic {
|
|||||||
|
|
||||||
async fn on_broadcast(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event_out: rlnl::event_code::NetworkEvent, event_in: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, data: &Option<Box<dyn crate::Broadcastable>>, _skip_user: bool) -> bool {
|
async fn on_broadcast(&self, generic: &crate::matches::GenericGamemodeEngine<Self>, _user_id: i32, _event_out: rlnl::event_code::NetworkEvent, event_in: rlnl::event_code::NetworkEvent, _property: literustlib::packet::Property, data: &Option<Box<dyn crate::Broadcastable>>, _skip_user: bool) -> bool {
|
||||||
match (event_in, data) {
|
match (event_in, data) {
|
||||||
|
(rlnl::event_code::NetworkEvent::DamageCube, Some(data)) => {
|
||||||
|
let maybe_cube_dmg = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::DestroyCubesFull>(data.as_ref());
|
||||||
|
if let Some(cube_damage) = maybe_cube_dmg {
|
||||||
|
let pseudo = rlnl::events::ingame::DestroyCubeNoEffect {
|
||||||
|
shooting_machine_id: cube_damage.shooting_machine_id,
|
||||||
|
hit_machine_id: cube_damage.hit_machine_id,
|
||||||
|
target_type: cube_damage.target_type,
|
||||||
|
num_hits: cube_damage.num_hit_cubes,
|
||||||
|
hit_cubes: cube_damage.hit_cubes.clone(),
|
||||||
|
};
|
||||||
|
match cube_damage.target_type {
|
||||||
|
rlnl::types::TargetType::TeamBase => {
|
||||||
|
let actual_damage_data = |cubes: Vec<rlnl::types::CubeState>| {
|
||||||
|
let mut cube_dmg = cube_damage.to_owned();
|
||||||
|
cube_dmg.num_hit_cubes = cubes.len() as _;
|
||||||
|
cube_dmg.hit_cubes = cubes;
|
||||||
|
crate::matches::RlnlPacket {
|
||||||
|
event: rlnl::event_code::NetworkEvent::DestroyCubesFull,
|
||||||
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
|
data: Box::new(cube_dmg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.do_team_base_stealing(&pseudo, generic, actual_damage_data).await;
|
||||||
|
false
|
||||||
|
},
|
||||||
|
rlnl::types::TargetType::EqualizerCrystal => {
|
||||||
|
self.do_equalizer_damage(&pseudo, generic).await;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
//log::info!("Got DamageCube with target_type {:?}", cube_damage.target_type);
|
||||||
|
true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("Got DamageCube event with bad serialization type");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
},
|
||||||
(rlnl::event_code::NetworkEvent::DamageCubeNoEffect, Some(data)) => {
|
(rlnl::event_code::NetworkEvent::DamageCubeNoEffect, Some(data)) => {
|
||||||
let maybe_cube_dmg = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::DestroyCubeNoEffect>(data.as_ref());
|
let maybe_cube_dmg = <dyn core::any::Any>::downcast_ref::<rlnl::events::ingame::DestroyCubeNoEffect>(data.as_ref());
|
||||||
if let Some(cube_damage) = maybe_cube_dmg {
|
if let Some(cube_damage) = maybe_cube_dmg {
|
||||||
if matches!(cube_damage.target_type, rlnl::types::TargetType::TeamBase) {
|
match cube_damage.target_type {
|
||||||
self.do_team_base_stealing(cube_damage, generic).await;
|
rlnl::types::TargetType::TeamBase => {
|
||||||
false
|
let actual_damage_data = |cubes: Vec<rlnl::types::CubeState>| {
|
||||||
} else {
|
let mut cube_dmg = cube_damage.to_owned();
|
||||||
//log::info!("Got non-base DamageCubeNoEffect {:?}", cube_damage.target_type);
|
cube_dmg.num_hits = cubes.len() as _;
|
||||||
true
|
cube_dmg.hit_cubes = cubes;
|
||||||
|
crate::matches::RlnlPacket {
|
||||||
|
event: rlnl::event_code::NetworkEvent::DestroyCubeNoEffect,
|
||||||
|
property: literustlib::packet::Property::ReliableOrdered,
|
||||||
|
data: Box::new(cube_dmg.to_owned()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
//log::info!("Got DamageCubeNoEffect with target_type {:?}", cube_damage.target_type);
|
||||||
|
true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Got DamageCubeNoEffect event with bad serialization type");
|
log::warn!("Got DamageCubeNoEffect event with bad serialization type");
|
||||||
@@ -1167,7 +1520,7 @@ impl CustomGameLogic for BattleArenaLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do base charge tick
|
// do base charge tick
|
||||||
let base_tick_info = self.base_tracking.tick(&tick_info, &self.crystals, generic, self.capture_tracking.points.len(), self.config.protonium_health as u32).await;
|
let base_tick_info = self.base_tracking.tick(&tick_info, &self.crystals, generic, self.capture_tracking.points.len(), self.config.protonium_health as u32, &self.config).await;
|
||||||
if let Some((winning_team, win_mode)) = base_tick_info.win {
|
if let Some((winning_team, win_mode)) = base_tick_info.win {
|
||||||
self.do_win(winning_team, win_mode, generic).await;
|
self.do_win(winning_team, win_mode, generic).await;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user