diff --git a/Cargo.lock b/Cargo.lock index 103ec4d..dcb675b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2424,6 +2424,22 @@ dependencies = [ "zip", ] +[[package]] +name = "oj_factory_api" +version = "1.1.0" +dependencies = [ + "actix-web", + "base64", + "clap", + "env_logger", + "git-version", + "libfj", + "log", + "oj_rc_core", + "oj_rc_factory", + "serde_json", +] + [[package]] name = "oj_polariton_auth" version = "1.1.0" diff --git a/assets/robocraft/config.json b/assets/robocraft/config.json index 399c36b..2b34dbf 100644 --- a/assets/robocraft/config.json +++ b/assets/robocraft/config.json @@ -12264,7 +12264,8 @@ "active": true, "protonium": true, "visibility": "None", - "ignore_in_weapon_list": true + "ignore_in_weapon_list": true, + "health": 10000 }, "spriteName": "CubeThumb_LightChassis_Cube", "nameStrKey": "strProtoniumCrystalName", diff --git a/rc_core/src/cubes/locations_of.rs b/rc_core/src/cubes/locations_of.rs index 09e1163..5ab5645 100644 --- a/rc_core/src/cubes/locations_of.rs +++ b/rc_core/src/cubes/locations_of.rs @@ -42,33 +42,41 @@ impl CubeLocationsParser { } } + fn locations_sorted_by_distance_from_point(cubes: &[super::parser::Cube], point: (u8, u8, u8), locations_of_id: u32) -> Vec { + 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.into_iter() + .filter(|x| x.id == locations_of_id) + .map(|cube| { + 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(); + (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 { match super::parser::Cube::parse_list(r) { Ok(cubes) => { if let Some(target) = cubes.iter().find(|x| x.id == distance_to_id) { - let target_x = target.x as f32; - let target_y = target.y as f32; - let target_z = target.z as f32; - let mut relevant_cubes: Vec<(f32, CubeLocationInfo)> = cubes.into_iter() - .filter(|x| x.id == locations_of_id) - .map(|cube| { - 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(); - (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() + Self::locations_sorted_by_distance_from_point( + &cubes, + (target.x, target.y, target.z), + locations_of_id, + ) } else { log::warn!("No cube with id {} to calculate distance", distance_to_id); cubes.into_iter() @@ -89,4 +97,20 @@ impl CubeLocationsParser { } } } + + pub fn locations_of_by_distance_from(&self, r: &mut dyn std::io::Read, locations_of_id: u32, from: (u8, u8, u8)) -> Vec { + match super::parser::Cube::parse_list(r) { + Ok(cubes) => { + Self::locations_sorted_by_distance_from_point( + &cubes, + from, + locations_of_id, + ) + } + Err(e) => { + log::error!("Failed to parse cube data to find cube locations: {}", e); + Vec::default() + } + } + } } diff --git a/rc_core/src/persist/combat.rs b/rc_core/src/persist/combat.rs index 0c0a978..1cc9ca9 100644 --- a/rc_core/src/persist/combat.rs +++ b/rc_core/src/persist/combat.rs @@ -336,7 +336,7 @@ fn default_game_modes() -> GameModes { respawn_heal_duration: 10.0, respawn_full_heal_duration: 0.5, kill_limit: 0, - game_time_m: 2, + game_time_m: 20, }, elimination: GameMode { respawn_heal_duration: 10.0, @@ -622,6 +622,7 @@ fn default_multiplayer() -> super::MultiplayerConfig { autostart_after_s: 180, network: super::multiplayer::default_net_conf(), fakes: super::multiplayer::default_fake_users(), + filler: super::multiplayer::default_filler_users(), battle_arena: super::multiplayer::default_ba_conf(), pit_config: super::multiplayer::default_pit_conf(), team_death_match: super::multiplayer::default_tdm_conf(), diff --git a/rc_core/src/persist/config/cubes_json.rs b/rc_core/src/persist/config/cubes_json.rs index 7067987..9f85777 100644 --- a/rc_core/src/persist/config/cubes_json.rs +++ b/rc_core/src/persist/config/cubes_json.rs @@ -488,6 +488,14 @@ impl super::ConfigProvider for CubeConfig { }).collect() } + fn filler_players(&self) -> Vec { + self.battle.multiplayer.filler.iter().map(|player| super::FakePlayer { + team: player.team, + vehicle: player.vehicle.into_conf(), + implementation: player.implementation.clone().to_config(), + }).collect() + } + fn energy(&self) -> super::EnergyConfig { super::EnergyConfig { refill_rate: self.battle.energy.refill_rate_per_s, diff --git a/rc_core/src/persist/config/traits.rs b/rc_core/src/persist/config/traits.rs index 19c82f0..644520b 100644 --- a/rc_core/src/persist/config/traits.rs +++ b/rc_core/src/persist/config/traits.rs @@ -34,6 +34,7 @@ pub trait ConfigProvider { fn maps(&self) -> std::collections::HashMap; fn url_links(&self) -> LinksConfig; fn fake_players(&self) -> Vec; + fn filler_players(&self) -> Vec; fn energy(&self) -> EnergyConfig; fn ba_settings(&self) -> BattleArenaResolver; fn pit_settings(&self) -> PitSettings; diff --git a/rc_core/src/persist/multiplayer.rs b/rc_core/src/persist/multiplayer.rs index 71465c2..df77421 100644 --- a/rc_core/src/persist/multiplayer.rs +++ b/rc_core/src/persist/multiplayer.rs @@ -9,6 +9,8 @@ pub struct MultiplayerConfig { pub network: NetworkConf, #[serde(default = "default_fake_users")] pub fakes: Vec, + #[serde(default = "default_filler_users")] + pub filler: Vec, #[serde(default = "default_ba_conf")] pub battle_arena: BattleArenaConfig, #[serde(default = "default_pit_conf")] @@ -166,6 +168,10 @@ pub(super) fn default_fake_users() -> Vec { ] } +pub(super) fn default_filler_users() -> Vec { + default_fake_users() +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(tag = "impl")] pub enum ClientEmulation { @@ -242,7 +248,7 @@ fn default_ba_base() -> super::garage::PrefabVehicle { } fn default_crystal_health() -> u32 { - 1_000 + 10_000 } fn default_respawn_time() -> u64 { diff --git a/rc_core/src/persist/user/account_json.rs b/rc_core/src/persist/user/account_json.rs index 9a48951..22fb658 100644 --- a/rc_core/src/persist/user/account_json.rs +++ b/rc_core/src/persist/user/account_json.rs @@ -8,6 +8,7 @@ pub struct AccountProvider { cubes: std::sync::Arc>, garage_upgrades: std::sync::Arc, fake_players: std::sync::Arc>, + filler_players: std::sync::Arc>, auto_signups: bool, cdn: std::sync::Arc, auth: std::sync::Arc, @@ -30,6 +31,7 @@ impl AccountProvider { cubes: std::sync::Arc::new(>::ids(conf)), garage_upgrades: std::sync::Arc::new(>::garage_upgrades(conf)), fake_players: std::sync::Arc::new(>::fake_players(conf)), + filler_players: std::sync::Arc::new(>::filler_players(conf)), auto_signups: server_settings.auto_signup, cdn: std::sync::Arc::new(server_settings.cdn_url), auth: std::sync::Arc::new(server_settings.auth_url), @@ -113,6 +115,7 @@ impl super::UserProvider for AccountProvider { cubes: self.cubes.clone(), garage_upgrades: self.garage_upgrades.clone(), fake_players: self.fake_players.clone(), + filler_players: self.filler_players.clone(), cdn: self.cdn.clone(), auth: self.auth.clone(), intercom: self.intercom.clone(), @@ -151,6 +154,7 @@ impl super::UserProvider for AccountProvider { cubes: self.cubes.clone(), garage_upgrades: self.garage_upgrades.clone(), fake_players: self.fake_players.clone(), + filler_players: self.filler_players.clone(), cdn: self.cdn.clone(), auth: self.auth.clone(), intercom: self.intercom.clone(), @@ -316,6 +320,7 @@ pub(super) struct UserData { pub(super) cubes: std::sync::Arc>, pub(super) garage_upgrades: std::sync::Arc, pub(super) fake_players: std::sync::Arc>, + pub(super) filler_players: std::sync::Arc>, pub(super) cdn: std::sync::Arc, pub(super) auth: std::sync::Arc, pub(super) intercom: std::sync::Arc, diff --git a/rc_core/src/persist/user/lobby.rs b/rc_core/src/persist/user/lobby.rs index f3030e4..cb72411 100644 --- a/rc_core/src/persist/user/lobby.rs +++ b/rc_core/src/persist/user/lobby.rs @@ -58,6 +58,7 @@ impl super::LobbyUser for UserData { cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, chooser: &TeamChooser, + missing_players: usize, ) -> Result { let now = chrono::Utc::now().timestamp(); let guid = crate::persist::user::str_to_i64(&game.guid) @@ -73,7 +74,8 @@ impl super::LobbyUser for UserData { oj_rc_database::schema::multiplayer_game::GameType::Standard }; - let fake_players = self.generate_fake_players_data(guid, &players, factory, cpu_counter, weapon_lister, chooser).await?; + let forced_fake_players = self.generate_forced_fake_players_data(guid, &players, factory, cpu_counter, weapon_lister, chooser).await?; + let filler_players = self.generate_filler_players_data(guid, &players, factory, cpu_counter, weapon_lister, chooser, missing_players).await?; let game_dbo = oj_rc_database::schema::multiplayer_game::ActiveModel { id: oj_rc_database::sea_orm::ActiveValue::NotSet, @@ -95,6 +97,7 @@ impl super::LobbyUser for UserData { })?; let players_len = players.len(); + let forced_fake_players_len = forced_fake_players.len(); let players: Vec = players.into_iter() .enumerate() @@ -113,7 +116,7 @@ impl super::LobbyUser for UserData { variant: oj_rc_database::sea_orm::ActiveValue::Set(oj_rc_database::schema::multiplayer_game_player::ClientType::Client), } }) - .chain(fake_players.iter() + .chain(forced_fake_players.iter() .enumerate() .map(|(i, (fake, variant))| { oj_rc_database::schema::multiplayer_game_player::ActiveModel { @@ -131,6 +134,24 @@ impl super::LobbyUser for UserData { } }) ) + .chain(filler_players.iter() + .enumerate() + .map(|(i, (fake, variant))| { + oj_rc_database::schema::multiplayer_game_player::ActiveModel { + id: oj_rc_database::sea_orm::ActiveValue::NotSet, + user_id: oj_rc_database::sea_orm::ActiveValue::Set(None), // if ClientAI, they will be assigned to a user during game loading + game_id: oj_rc_database::sea_orm::ActiveValue::Set(game_dbo.id), + creation_time: oj_rc_database::sea_orm::ActiveValue::Set(now), + player_id: oj_rc_database::sea_orm::ActiveValue::Set(((i + players_len + forced_fake_players_len) as u8) as _), + team: oj_rc_database::sea_orm::ActiveValue::Set(fake.team), + group: oj_rc_database::sea_orm::ActiveValue::Set(None), + is_claimed: oj_rc_database::sea_orm::ActiveValue::Set(true), + public_id: oj_rc_database::sea_orm::ActiveValue::Set(fake.name.clone()), + display_name: oj_rc_database::sea_orm::ActiveValue::Set(fake.display_name.clone()), + variant: oj_rc_database::sea_orm::ActiveValue::Set(fake_impl_to_db(variant)), + } + }) + ) .collect(); self.db.insert_players(players).await.map_err(|e| { log::error!("Failed to create game players for {} through user_id {}: {}", game.guid, self.account.id, e); @@ -140,6 +161,8 @@ impl super::LobbyUser for UserData { ) })?; - Ok(super::FakePlayers { players: fake_players }) + Ok(super::FakePlayers { + players: forced_fake_players.into_iter().chain(filler_players).collect(), + }) } } diff --git a/rc_core/src/persist/user/multiplayer.rs b/rc_core/src/persist/user/multiplayer.rs index e45db7f..cd5b8b8 100644 --- a/rc_core/src/persist/user/multiplayer.rs +++ b/rc_core/src/persist/user/multiplayer.rs @@ -17,10 +17,11 @@ impl UserData { cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, chooser: &super::TeamChooser, + fake_players: &[crate::persist::config::FakePlayer], ) -> Result, polariton_server::operations::SimpleOpError> { - let mut fakes = Vec::with_capacity(self.fake_players.len()); + let mut fakes = Vec::with_capacity(fake_players.len()); let mut fake_i = real_players.len(); - for fake in self.fake_players.iter() { + for fake in fake_players.iter() { let vehicle = self.resolve_vehicle(&fake.vehicle, factory, weapon_lister, cpu_counter).await?; let out = ( crate::data::player_data::PlayerData { @@ -55,6 +56,47 @@ impl UserData { } Ok(fakes) } + + pub(super) async fn generate_forced_fake_players_data( + &self, + guid: i64, + real_players: &[super::PlayerLobbyDescriptor], + factory: &dyn oj_rc_factory::VehicleFactoryAdapter, + cpu_counter: &crate::cubes::CpuListParser, + weapon_lister: &crate::cubes::WeaponListParser, + chooser: &super::TeamChooser, + ) -> Result, polariton_server::operations::SimpleOpError> { + self.generate_fake_players_data( + guid, + real_players, + factory, + cpu_counter, + weapon_lister, + chooser, + &self.fake_players, + ).await + } + + pub(super) async fn generate_filler_players_data( + &self, + guid: i64, + real_players: &[super::PlayerLobbyDescriptor], + factory: &dyn oj_rc_factory::VehicleFactoryAdapter, + cpu_counter: &crate::cubes::CpuListParser, + weapon_lister: &crate::cubes::WeaponListParser, + chooser: &super::TeamChooser, + count: usize, + ) -> Result, polariton_server::operations::SimpleOpError> { + self.generate_fake_players_data( + guid, + real_players, + factory, + cpu_counter, + weapon_lister, + chooser, + &self.filler_players[0..count], + ).await + } } #[async_trait::async_trait] diff --git a/rc_core/src/persist/user/traits.rs b/rc_core/src/persist/user/traits.rs index e646a32..745a798 100644 --- a/rc_core/src/persist/user/traits.rs +++ b/rc_core/src/persist/user/traits.rs @@ -265,7 +265,7 @@ pub trait LobbyUser { fn user_id(&self) -> i32; async fn player_data(&self, cpu_counter: &crate::cubes::CpuListParser) -> Result; async fn team_chooser(&self, game: &GameDescriptor) -> super::TeamChooser; - async fn start_game(&self, game: GameDescriptor, players: Vec, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, team_chooser: &super::TeamChooser) -> Result; + async fn start_game(&self, game: GameDescriptor, players: Vec, factory: &dyn oj_rc_factory::VehicleFactoryAdapter, cpu_counter: &crate::cubes::CpuListParser, weapon_lister: &crate::cubes::WeaponListParser, team_chooser: &super::TeamChooser, missing_players: usize) -> Result; } pub struct FakePlayers { diff --git a/rc_lobby_room/src/lobby.rs b/rc_lobby_room/src/lobby.rs index a50c3c9..f042c4b 100644 --- a/rc_lobby_room/src/lobby.rs +++ b/rc_lobby_room/src/lobby.rs @@ -217,11 +217,12 @@ impl QueueHandler { display_name: x.player.display_name.clone(), }).collect(); - match user.start_game(game_desc, player_descs, factory.as_ref(), &cpu_counter, &weapon_guesser, &team_picker).await { + let missing = users_per_game.saturating_sub(players.len()); + + match user.start_game(game_desc, player_descs, factory.as_ref(), &cpu_counter, &weapon_guesser, &team_picker, missing).await { Ok(fakes) => { - let missing = users_per_game.saturating_sub(players.len()); let player_datas = players.iter().map(|x| x.player.clone()) - .chain(fakes.players.into_iter().take(missing).map(|(desc, _emu)| desc),) + .chain(fakes.players.into_iter().map(|(desc, _emu)| desc),) .collect(); let enter_battle_ev = crate::events::battle_enter::BattleEnter { host: hostname.clone(), diff --git a/rc_multiplayer/src/matches/modes/battle_arena.rs b/rc_multiplayer/src/matches/modes/battle_arena.rs index ed15412..6c59e57 100644 --- a/rc_multiplayer/src/matches/modes/battle_arena.rs +++ b/rc_multiplayer/src/matches/modes/battle_arena.rs @@ -721,7 +721,7 @@ struct BaseTickInfo { } impl BaseTracker { - const DOMINATING_MULT: f32 = 10.0; + const DOMINATING_MULT: f32 = 4.0; fn new<'a>(bases_iter: impl std::iter::Iterator, crystals: &[oj_rc_core::cubes::CubeLocationInfo], ba_config: &oj_rc_core::data::battle_arena_config::BattleArenaData) -> Self { let mut bases = std::collections::HashMap::new(); @@ -735,19 +735,40 @@ impl BaseTracker { } } + fn apply_partial_base_heal(&self, crystals: &[oj_rc_core::cubes::CubeLocationInfo], crystal_health: u32, base_id: u8, base: &BaseInfo, old_index: usize, new_index: usize) -> rlnl::events::HealedCubes { + //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); + } + rlnl::events::HealedCubes { + healed_machine: base_id as u16, + type_performing_healing: rlnl::types::TargetType::TeamBase, + target_type: rlnl::types::TargetType::TeamBase, + 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, + }) + .collect(), + } + } + async fn tick(&self, tick_info: &PointTickInfo, crystals: &[oj_rc_core::cubes::CubeLocationInfo], generic: &crate::matches::GenericGamemodeEngine, 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 + id: u8::MAX, // ignored? + attacking_team: dominant_team as i8, + defending_team: -1, // ignored? + //defending_team: (((dominant_team as usize) + 1) % generic.map_config.bases.len()) as i8 }, true ).await; @@ -795,7 +816,8 @@ impl BaseTracker { ], } } else { - let target_crystals = &crystals[old_index..new_index]; + self.apply_partial_base_heal(crystals, crystal_health, *base_id, tracked_base, old_index, new_index) + /*let target_crystals = &crystals[old_index..new_index]; for crystal_i in old_index..new_index { tracked_base.crystals_healths[crystal_i].store(u8::MAX, std::sync::atomic::Ordering::Relaxed); } @@ -811,7 +833,7 @@ impl BaseTracker { damage: crystal_health as i32, }) .collect(), - } + }*/ }; generic.broadcast( @@ -974,6 +996,7 @@ impl BattleArenaLogic { 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)); Self { respawn_full_heal_duration: config.respawn_full_heal_duration, respawn_heal_duration: config.respawn_heal_duration, @@ -1147,7 +1170,7 @@ impl BattleArenaLogic { } } - async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine, actual_damage_data: impl FnOnce(Vec) -> crate::matches::RlnlPacket) { + async fn do_team_base_stealing(&self, cube_damage: &rlnl::events::ingame::DestroyCubeNoEffect, generic: &crate::matches::GenericGamemodeEngine, _actual_damage_data: impl FnOnce(Vec) -> 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()); @@ -1178,30 +1201,49 @@ impl BattleArenaLogic { }, true, ).await;*/ - let actual_damage_data = actual_damage_data(valid_cubes); - generic.broadcast( + //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; - generic.broadcast( + ).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 { if let Some(base) = self.base_tracking.bases.get(&team_id) { //log::info!("Player {} stole {} crystals", cube_damage.shooting_machine_id, total_destroyed); - base.cube_index.fetch_add(total_destroyed as f32, std::sync::atomic::Ordering::SeqCst); - generic.broadcast( + let total_destroyed_f32 = total_destroyed as f32; + let old_float_index = base.cube_index.fetch_add(total_destroyed_f32, std::sync::atomic::Ordering::SeqCst); + let new_float_index = old_float_index + total_destroyed_f32; + let old_index = (old_float_index.ceil() as usize).clamp(0, self.crystals.len()); + let new_index = (new_float_index.ceil() as usize).clamp(0, self.crystals.len()); + let payload = self.base_tracking.apply_partial_base_heal( + &self.crystals, + self.config.protonium_health as u32, + team_id, + base, + 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, + literustlib::packet::Property::ReliableOrdered, + &payload, + true, ).await; } } else { @@ -1470,7 +1512,7 @@ impl CustomGameLogic for BattleArenaLogic { } }; self.do_team_base_stealing(&pseudo, generic, actual_damage_data).await; - false + true }, rlnl::types::TargetType::EqualizerCrystal => { self.do_equalizer_damage(&pseudo, generic).await; @@ -1502,7 +1544,7 @@ impl CustomGameLogic for BattleArenaLogic { } }; self.do_team_base_stealing(cube_damage, generic, &actual_damage_data).await; - false + true }, rlnl::types::TargetType::EqualizerCrystal => { self.do_equalizer_damage(cube_damage, generic).await;