diff --git a/rc_services_room/src/data/campaign.rs b/rc_services_room/src/data/campaign.rs new file mode 100644 index 0000000..9d2927a --- /dev/null +++ b/rc_services_room/src/data/campaign.rs @@ -0,0 +1,301 @@ +use polariton::operation::Typed; + +pub struct CampaignsGameParameters { + pub campaigns: Vec, +} + +impl CampaignsGameParameters { + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + writer.write_all(&(self.campaigns.len() as i32).to_le_bytes())?; + let mut total_len = 4; + for campaign in self.campaigns.iter() { + total_len += campaign.dump(writer)?; + } + Ok(total_len) + } + + pub fn as_transmissible(&self) -> Typed { + let mut buf = Vec::new(); + self.dump(&mut std::io::Cursor::new(&mut buf)).unwrap(); + Typed::Bytes(buf.into()) + } +} + +pub struct CampaignParameters { + pub id: String, + pub excluded_cubes: Vec, // encoded to hex strings + pub categories: Vec, + pub min_cpu: i32, + pub max_cpu: i32, + pub name: String, + pub description: String, + pub image: String, + pub rules: Vec, + pub parameters: Vec>, + pub difficulties: Vec, + pub completed: Vec, + pub map: String, +} + +impl CampaignParameters { + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + let mut total_len = super::write_str_for_binreader(&self.id, writer)?; + writer.write_all(&(self.excluded_cubes.len() as i32).to_le_bytes())?; + total_len += 4; + for excluded_cube in self.excluded_cubes.iter() { + let s = super::cube_id_to_str(*excluded_cube); + total_len += super::write_str_for_binreader(&s, writer)?; + } + writer.write_all(&(self.categories.len() as i32).to_le_bytes())?; + total_len += 4; + for cat in self.categories.iter() { + total_len += super::write_str_for_binreader(cat.as_str(), writer)?; + } + writer.write_all(&self.min_cpu.to_le_bytes())?; + writer.write_all(&self.max_cpu.to_le_bytes())?; + total_len += 8; + total_len += super::write_str_for_binreader(&self.name, writer)?; + total_len += super::write_str_for_binreader(&self.description, writer)?; + total_len += super::write_str_for_binreader(&self.image, writer)?; + writer.write_all(&(self.rules.len() as i32).to_le_bytes())?; + total_len += 4; + for rule in self.rules.iter() { + total_len += super::write_str_for_binreader(rule, writer)?; + } + writer.write_all(&(self.parameters.len() as i32).to_le_bytes())?; + total_len += 4; + for param_vec in self.parameters.iter() { + writer.write_all(&(param_vec.len() as i32).to_le_bytes())?; + total_len += 4; + for param in param_vec.iter() { + total_len += super::write_str_for_binreader(param, writer)?; + } + } + writer.write_all(&(self.difficulties.len() as i32).to_le_bytes())?; + total_len += 4; + for difficulty in self.difficulties.iter() { + writer.write_all(&(CampaignDifficultyData::WRITE_BYTES_LEN as i32).to_le_bytes())?; + total_len += 4 + difficulty.dump(writer)?; + } + writer.write_all(&(self.completed.len() as i32).to_le_bytes())?; + total_len += 4; + for completion in self.completed.iter() { + total_len += completion.dump(writer)?; + } + total_len += super::write_str_for_binreader(&self.map, writer)?; + Ok(total_len) + } +} + +pub struct CampaignDifficultyData { + pub level: i32, + pub lives: i32, + pub auto_heal: bool, + pub single_wave_bonus: i32, + pub initial_health_boost: f32, + pub health_boost_wave_increase: f32, + pub initial_damage_boost: f32, + pub damage_boost_wave_increase: f32, +} + +impl CampaignDifficultyData { + const WRITE_BYTES_LEN: usize = 29; + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + writer.write_all(&self.level.to_le_bytes())?; + writer.write_all(&self.lives.to_le_bytes())?; + writer.write_all(&[self.auto_heal as u8])?; + writer.write_all(&self.single_wave_bonus.to_le_bytes())?; + writer.write_all(&self.initial_health_boost.to_le_bytes())?; + writer.write_all(&self.health_boost_wave_increase.to_le_bytes())?; + writer.write_all(&self.initial_damage_boost.to_le_bytes())?; + writer.write_all(&self.damage_boost_wave_increase.to_le_bytes())?; + Ok(Self::WRITE_BYTES_LEN) + } +} + +pub struct CampaignCompletionData { + pub index: i32, + pub wave: i32, + pub difficulty: bool, +} + +impl CampaignCompletionData { + const WRITE_BYTES_LEN: usize = 9; + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + writer.write_all(&self.index.to_le_bytes())?; + writer.write_all(&self.wave.to_le_bytes())?; + writer.write_all(&[self.difficulty as u8])?; + Ok(Self::WRITE_BYTES_LEN) + } +} + +pub struct LiveCampaignWaves { + pub waves: Vec, +} + +impl LiveCampaignWaves { + pub fn as_transmissible(&self) -> Typed { + Typed::HashMap(self.waves.iter().flat_map(|waves| waves.as_transmissible_key_val()).collect::>().into()) + } +} + +pub struct WavesData { + pub id: String, + pub waves: Vec, + pub campaign_type: CampaignType, +} + +impl WavesData { + pub fn as_transmissible_key_val(&self) -> [(Typed, Typed); 3] { + [ + (Typed::Str(format!("wavesNumberInCurrentCampaign_{}", self.id).into()), Typed::Int(self.waves.len() as _)), + (Typed::Str(self.id.clone().into()), Typed::HashMap(self.waves.iter().enumerate().flat_map(|(i, wave)| wave.as_transmissible_key_val(i)).collect::>().into())), + (Typed::Str(format!("campaignType_{}", self.id).into()), Typed::Int(self.campaign_type as _)), + ] + } +} + +pub struct WaveData { + pub robots_in_wave: Vec, +} + +impl WaveData { + pub fn as_transmissible_key_val(&self, index: usize) -> [(Typed, Typed); 2] { + [ + (Typed::Str(format!("numberOfDifferentRobotsInCurrentWave_{}", index).into()), Typed::Int(self.robots_in_wave.len() as _)), + (Typed::Int(index as _), Typed::HashMap(self.robots_in_wave.iter().enumerate().map(|(i, robot)| (Typed::Int(i as _), robot.as_transmissible())).collect::>().into())), + ] + } +} + +pub struct WaveRobotData { + pub name: String, + pub weapon: String, + pub movement: String, + pub rank: String, + pub count: i32, +} + +impl WaveRobotData { + pub fn as_transmissible(&self) -> Typed { + Typed::HashMap(vec![ + (Typed::Str("RobotName".into()), Typed::Str(self.name.clone().into())), + (Typed::Str("RobotWeapon".into()), Typed::Str(self.weapon.clone().into())), + (Typed::Str("RobotMovementPart".into()), Typed::Str(self.movement.clone().into())), + (Typed::Str("RobotRank".into()), Typed::Str(self.rank.clone().into())), + (Typed::Str("RobotCount".into()), Typed::Int(self.count)), + ].into()) + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +pub enum CampaignType { + TimedElimination = 0, + Survival = 1, + Elimination = 2, +} + +pub struct GameModeVersionParameters { + pub current_version: i32, + pub is_locked: std::collections::HashMap, +} + +impl GameModeVersionParameters { + pub fn as_transmissible(&self) -> Typed { + Typed::HashMap(vec![ + (Typed::Str("CurrentVersionNumber".into()), Typed::Int(self.current_version)), + (Typed::Str("LockedCampaignsInfo".into()), Typed::HashMap(self.is_locked.iter().map(|(key, val)| (Typed::Str(key.into()), Typed::Bool(*val))).collect::>().into())), + ].into()) + } +} + +pub struct CampaignWavesDifficultyData { + pub difficulty: CampaignDifficultyData, + pub waves: Vec, +} + +impl CampaignWavesDifficultyData { + pub fn as_transmissible(&self) -> Typed { + let mut buf = Vec::new(); + self.dump(&mut std::io::Cursor::new(&mut buf)).unwrap(); + Typed::Bytes(buf.into()) + } + + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + writer.write_all(&(CampaignDifficultyData::WRITE_BYTES_LEN as i32).to_le_bytes())?; + self.difficulty.dump(writer)?; + writer.write_all(&(self.waves.len() as i32).to_le_bytes())?; + let mut waves_total_len = 4; + for wave in self.waves.iter() { + waves_total_len += wave.dump(writer)?; + } + Ok(4 + CampaignDifficultyData::WRITE_BYTES_LEN + waves_total_len) + } +} + +pub struct CompleteWaveData { + pub player_spawn_location: i32, + pub robots_in_wave: Vec, + pub kill_target: i32, + pub time_min: i32, + pub time_max: i32, +} + +impl CompleteWaveData { + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + writer.write_all(&self.player_spawn_location.to_le_bytes())?; + writer.write_all(&(self.robots_in_wave.len() as i32).to_le_bytes())?; + let mut robots_total_len = 4; + for robot in self.robots_in_wave.iter() { + robots_total_len += robot.dump(writer)?; + } + writer.write_all(&self.kill_target.to_le_bytes())?; + writer.write_all(&self.time_min.to_le_bytes())?; + writer.write_all(&self.time_max.to_le_bytes())?; + Ok(16 + robots_total_len) + } +} + +pub struct CompleteWaveRobotData { + pub name: String, + pub robot_data: Vec, + pub colour_data: Vec, + pub time_to_spawn: i32, + pub kills_to_spawn: i32, + pub time_to_despawn: i32, + pub kills_to_despawn: i32, + pub initial_robot_amount: i32, + pub periodic_robot_amount: i32, + pub spawn_interval: i32, + pub min_robot_amount: i32, + pub max_robot_amount: i32, + pub is_boss: bool, + pub is_kill_requirement: bool, +} + +impl CompleteWaveRobotData { + fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { + let mut total_len = super::write_str_for_binreader(&self.name, writer)?; + writer.write_all(&(self.robot_data.len() as i32).to_le_bytes())?; + total_len += 4; + writer.write_all(&self.robot_data)?; + total_len += self.robot_data.len(); + writer.write_all(&(self.colour_data.len() as i32).to_le_bytes())?; + total_len += 4; + writer.write_all(&self.colour_data)?; + total_len += self.colour_data.len(); + writer.write_all(&self.time_to_spawn.to_le_bytes())?; + writer.write_all(&self.kills_to_spawn.to_le_bytes())?; + writer.write_all(&self.time_to_despawn.to_le_bytes())?; + writer.write_all(&self.kills_to_despawn.to_le_bytes())?; + writer.write_all(&self.initial_robot_amount.to_le_bytes())?; + writer.write_all(&self.periodic_robot_amount.to_le_bytes())?; + writer.write_all(&self.spawn_interval.to_le_bytes())?; + writer.write_all(&self.min_robot_amount.to_le_bytes())?; + writer.write_all(&self.max_robot_amount.to_le_bytes())?; + writer.write_all(&[self.is_boss as u8])?; + writer.write_all(&[self.is_kill_requirement as u8])?; + Ok(total_len) + } +} diff --git a/rc_services_room/src/data/cube_list.rs b/rc_services_room/src/data/cube_list.rs index 12646e5..f6a86b7 100644 --- a/rc_services_room/src/data/cube_list.rs +++ b/rc_services_room/src/data/cube_list.rs @@ -127,3 +127,7 @@ impl ItemType { } } } + +pub fn item_key(category: super::weapon_list::ItemCategory, tier: ItemTier) -> i32 { + category.but_bigger() + (tier as i32) +} diff --git a/rc_services_room/src/data/mod.rs b/rc_services_room/src/data/mod.rs index 15e2519..28f333c 100644 --- a/rc_services_room/src/data/mod.rs +++ b/rc_services_room/src/data/mod.rs @@ -27,6 +27,7 @@ pub mod lobby; pub mod error_codes; pub mod game_mode; pub mod score_multipliers; +pub mod campaign; pub(self) fn encode_7_bit_i32(mut src: i32) -> Vec { if src == 0 { return vec![0] } @@ -49,3 +50,7 @@ pub(self) fn write_str_for_binreader(s: &str, writer: &mut dyn std::io::Write) - total_len += writer.write(s_bytes)?; Ok(total_len) } + +pub(self) fn cube_id_to_str(id: u32) -> String { + hex::encode(id.to_be_bytes()).into() +} diff --git a/rc_services_room/src/data/score_multipliers.rs b/rc_services_room/src/data/score_multipliers.rs index cc4d4ba..ad8deb5 100644 --- a/rc_services_room/src/data/score_multipliers.rs +++ b/rc_services_room/src/data/score_multipliers.rs @@ -14,6 +14,7 @@ pub struct ScoreMultipliersData { impl ScoreMultipliersData { fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result { writer.write_all(&self.max_cpu.to_le_bytes())?; + writer.write_all(&(self.stat_multipliers.len() as i32).to_le_bytes())?; for (key, val) in self.stat_multipliers.iter() { writer.write_all(&(*key as u32).to_le_bytes())?; writer.write_all(&val.base.to_le_bytes())?; @@ -25,7 +26,7 @@ impl ScoreMultipliersData { writer.write_all(&self.defeat_score.to_le_bytes())?; writer.write_all(&self.victory_score.to_le_bytes())?; writer.write_all(&self.max_score_ratio.to_le_bytes())?; - Ok(28 + (12 * self.stat_multipliers.len())) + Ok(32 + (12 * self.stat_multipliers.len())) } pub fn as_transmissible(&self) -> Typed { diff --git a/rc_services_room/src/operations/all_customisations_info.rs b/rc_services_room/src/operations/all_customisations_info.rs index ed02faf..41ebdde 100644 --- a/rc_services_room/src/operations/all_customisations_info.rs +++ b/rc_services_room/src/operations/all_customisations_info.rs @@ -36,7 +36,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -44,7 +44,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn_BlackHole".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn_BlackHole".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -52,7 +52,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn_Lander".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn_Lander".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -60,7 +60,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn_Lootcrate".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn_Lootcrate".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -68,7 +68,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn_Warp".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn_Warp".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -76,7 +76,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn_Present".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn_Present".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -84,7 +84,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Spawn_EasterEgg".to_string(), localised_name: "strSpawnFXWarpIn".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Respawn_WarpIn".to_string(), + simulation_prefab: "Spawn_EasterEgg".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -98,7 +98,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -106,7 +106,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion_Toon".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion_Toon".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -114,7 +114,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion_Feathers_Rainbow".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion_Feathers_Rainbow".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -122,7 +122,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion_Nuclear".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion_Nuclear".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -130,7 +130,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion_Warp".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion_Warp".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -138,7 +138,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion_BlackHole".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion_BlackHole".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), @@ -146,7 +146,7 @@ pub(super) fn all_customisations_provider() -> SimpleFunc<216, crate::UserTy, im id: "Explosion_Firework".to_string(), localised_name: "strDeathFXEmergencyWarp".to_string(), skin_scene_name: "Splash_Loading_Screen".to_string(), - simulation_prefab: "Death_WarpOut".to_string(), + simulation_prefab: "Explosion_Firework".to_string(), preview_image_name: "RC_Splash_Screen_01_Japanese".to_string(), is_default: true, }.as_transmissible(), diff --git a/rc_services_room/src/operations/client_config.rs b/rc_services_room/src/operations/client_config.rs index 505f7b8..06d9b3f 100644 --- a/rc_services_room/src/operations/client_config.rs +++ b/rc_services_room/src/operations/client_config.rs @@ -13,14 +13,14 @@ pub(super) fn client_config_provider() -> SimpleFunc<34, crate::UserTy, impl (Fn val_ty: TypePrefix::HashMap, // hashtable items: vec![ (Typed::Str("GameplaySettings".into()), GameplaySettings { - show_tutorial_after_date: "2025-01-01".to_owned(), - health_threshold: 10.0, + show_tutorial_after_date: "2030-01-01".to_owned(), + health_threshold: 0.20, microbot_sphere: 10.0, - misfire_angle: 20.0, + misfire_angle: 10.0, shield_dps: 100, shield_hps: 2_000, request_review_level: 10_000, - critical_ratio: 10.0, + critical_ratio: 5.0, cross_promo_image: "https://git.ngram.ca/assets/img/logo.png".to_owned(), cross_promo_link: "https://git.ngram.ca/OpenJam/servers".to_owned(), }.as_transmissible()) diff --git a/rc_services_room/src/operations/cpu_limits_config.rs b/rc_services_room/src/operations/cpu_limits_config.rs index 252ee7e..d6646c0 100644 --- a/rc_services_room/src/operations/cpu_limits_config.rs +++ b/rc_services_room/src/operations/cpu_limits_config.rs @@ -12,8 +12,8 @@ pub(super) fn cpu_config_provider() -> SimpleFunc<75, crate::UserTy, impl (Fn(Pa premium_for_life_cosmetic_gpu: 12, premium_cosmetic_cpu: 6, no_premium_cosmetic_cpu: 3, - max_regular_health: 2_000_000, - max_megabot_health: 200_000_000, + max_regular_health: 200_000_000, + max_megabot_health: 2_000_000_000, }.as_transmissible()); Ok(params.into()) }) diff --git a/rc_services_room/src/operations/damage_boost_stats.rs b/rc_services_room/src/operations/damage_boost_stats.rs index 9c2d141..737a11f 100644 --- a/rc_services_room/src/operations/damage_boost_stats.rs +++ b/rc_services_room/src/operations/damage_boost_stats.rs @@ -14,9 +14,9 @@ pub(super) fn damage_boost_provider() -> SimpleFunc<163, crate::UserTy, impl (Fn items: vec![ (Typed::Str("damageBoost".into()), DamageBoostData { damage_map: vec![ - (100, 1000.0), - (1000, 100.0), + (0, 1.0), (2000, 1.0), + (10_000, 1.0), ], }.as_transmissible()) ], diff --git a/rc_services_room/src/operations/machine.rs b/rc_services_room/src/operations/machine.rs index 4274f40..dfee371 100644 --- a/rc_services_room/src/operations/machine.rs +++ b/rc_services_room/src/operations/machine.rs @@ -76,16 +76,3 @@ pub(super) fn garage_machine_save_provider() -> SimpleFunc<41, crate::UserTy, im }) } -pub const DEFAULT_WEAPON_ORDER_PARAM_KEY: u8 = 138; - -pub(super) fn weapon_order_provider() -> SimpleFunc<118, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, user: &crate::UserTy| { - let mut params = params.to_dict(); - let lock = user.read().unwrap(); - let user_info = lock.user()?; - let weapon_order = user_info.slot_by_id(user_info.selected_garage_slot() as i32)?.weapon_order; - params.insert(DEFAULT_WEAPON_ORDER_PARAM_KEY, weapon_order); - Ok(params.into()) - }) -} - diff --git a/rc_services_room/src/operations/mod.rs b/rc_services_room/src/operations/mod.rs index 94f5be0..3a439de 100644 --- a/rc_services_room/src/operations/mod.rs +++ b/rc_services_room/src/operations/mod.rs @@ -79,6 +79,7 @@ mod validate_machine; mod game_mode_config; mod score_multipliers_config; mod player_robot_rank; +mod weapon_order; use polariton_server::operations::OperationsHandler; @@ -150,7 +151,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .without_state(prebuilt_robots::garage_robot_data_provider()) .without_state(prebuilt_colours::garage_colour_combo_provider()) .without_state(robopass_preview_items::robopass_preview_provider()) - .without_state(singleplayer_campaigns::singleplayer_campaigns_provider()) + .without_state(singleplayer_campaigns::singleplayer_campaigns_provider(&init_ctx.cubes)) .without_state(purchases::pending_purchases_provider()) .without_state(building_xp_config::building_xp_config_provider()) .without_state(weapon_rating_static::weapon_rating_provider()) @@ -168,7 +169,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .without_state(machine::garage_machine_save_provider()) .without_state(polariton_server::operations::Ack::<32, _>::default()) // TODO handle SaveMachineColorRequest instead of ignoring it .without_state(polariton_server::operations::Ack::<45, _>::default()) // TODO handle UpdateThumbnailVersionRequest instead of ignoring it - .without_state(machine::weapon_order_provider()) + .without_state(weapon_order::weapon_order_provider(&init_ctx.cubes)) .without_state(regen_config::auto_regen_config_provider(&init_ctx.cubes)) .without_state(pageantry::after_battle_vote_thresholds_provider(&init_ctx.cubes)) .without_state(signup_time::user_signup_date_provider()) @@ -176,4 +177,8 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler .without_state(game_mode_config::game_mode_config_provider(&init_ctx.cubes)) .without_state(score_multipliers_config::tdm_ai_score_config_provider()) .without_state(player_robot_rank::player_robot_rank_provider()) + .without_state(validate_machine::validate_campaign_robot_provider()) + .without_state(singleplayer_campaigns::singleplayer_complete_campaign_provider(&init_ctx.cubes)) + .without_state(polariton_server::operations::Ack::<78, _>::default()) // TODO handle SaveCampaignGameAwardsRequest instead of ignoring it + .without_state(singleplayer_campaigns::singleplayer_save_complete_campaign_provider()) // TODO handle UpdatePlayerCompletedCampaignWaveRequest saving } diff --git a/rc_services_room/src/operations/power_bar_stats.rs b/rc_services_room/src/operations/power_bar_stats.rs index 1c0699a..824153a 100644 --- a/rc_services_room/src/operations/power_bar_stats.rs +++ b/rc_services_room/src/operations/power_bar_stats.rs @@ -7,8 +7,8 @@ pub(super) fn power_bar_provider() -> SimpleFunc<51, crate::UserTy, impl (Fn(Par SimpleFunc::new(|params, _| { let mut params = params.to_dict(); params.insert(PARAM_KEY, Typed::HashMap(vec![ - (Typed::Str("refillRatePerSecond".into()), Typed::Float(1000.0)), - (Typed::Str("powerForAllRobots".into()), Typed::Int(1_000_000 /* converted to u32 */)), + (Typed::Str("refillRatePerSecond".into()), Typed::Float(1255.0)), + (Typed::Str("powerForAllRobots".into()), Typed::Int(12_550 /* converted to u32 */)), ].into() )); Ok(params.into()) diff --git a/rc_services_room/src/operations/robot_customisations.rs b/rc_services_room/src/operations/robot_customisations.rs index 1a8b653..8678776 100644 --- a/rc_services_room/src/operations/robot_customisations.rs +++ b/rc_services_room/src/operations/robot_customisations.rs @@ -10,8 +10,8 @@ pub(super) fn bay_customisations_provider() -> SimpleFunc<218, crate::UserTy, im SimpleFunc::new(|params, _| { let mut params = params.to_dict(); params.insert(BAY_SKIN_KEY, Typed::Str("RC_MothershipSkin_Neptune_01".into())); - params.insert(SPAWN_EFFECT_KEY, Typed::Str("RE_todo_spawn_effect".into())); - params.insert(DEATH_EFFECT_KEY, Typed::Str("RE_todo_death_effect".into())); + params.insert(SPAWN_EFFECT_KEY, Typed::Str("Spawn_Warp".into())); + params.insert(DEATH_EFFECT_KEY, Typed::Str("Explosion_Warp".into())); Ok(params.into()) }) } diff --git a/rc_services_room/src/operations/singleplayer_campaigns.rs b/rc_services_room/src/operations/singleplayer_campaigns.rs index fa92501..b6159b3 100644 --- a/rc_services_room/src/operations/singleplayer_campaigns.rs +++ b/rc_services_room/src/operations/singleplayer_campaigns.rs @@ -1,22 +1,65 @@ -use polariton_server::operations::SimpleFunc; +use polariton_server::operations::{Immediate, SimpleFunc}; use polariton::operation::{ParameterTable, Typed}; +use crate::persist::config::ConfigProvider; + const CAMPAIGNS_BYTES_PARAM_KEY: u8 = 64; // list of bytes (serialised data) const CAMPAIGNS_WAVES_PARAM_KEY: u8 = 70; // hashtable const CAMPAIGNS_VERSIONS_PARAM_KEY: u8 = 69; // hashtable -pub(super) fn singleplayer_campaigns_provider() -> SimpleFunc<65, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { - SimpleFunc::new(|params, _| { +pub(super) fn singleplayer_campaigns_provider(conf: &crate::persist::config::ConfigImpl) -> Immediate<65, crate::UserTy> { + Immediate::new(|| { + let mut params = std::collections::HashMap::new(); + params.insert(CAMPAIGNS_BYTES_PARAM_KEY, conf.campaigns_parameters()); // first 4 bytes are i32 for length of the rest + //params.insert(CAMPAIGNS_BYTES_PARAM_KEY, Typed::Bytes(vec![0u8, 0u8, 0u8, 0u8].into())); // first 4 bytes are i32 for length of the rest + params.insert(CAMPAIGNS_WAVES_PARAM_KEY, conf.campaign_waves()); + //params.insert(CAMPAIGNS_WAVES_PARAM_KEY, Typed::HashMap(vec![].into())); + params.insert(CAMPAIGNS_VERSIONS_PARAM_KEY, conf.campaign_version()); + // params.insert(CAMPAIGNS_VERSIONS_PARAM_KEY, Typed::HashMap(vec![ + // (Typed::Str("CurrentVersionNumber".into()), Typed::Int(0)), + // (Typed::Str("LockedCampaignsInfo".into()), Typed::HashMap(vec![ + // (Typed::Str("0".into()), Typed::Bool(false.into())) + // ].into())), + // ].into())); + params.into() + }) +} + +const CAMPAIGN_ID_PARAM_KEY: u8 = 22; // string; in +const CAMPAIGN_DIFFICULTY_PARAM_KEY: u8 = 23; // i32; in +const CAMPAIGN_WAVES_PARAM_KEY: u8 = 75; // bytes; out + +pub(super) fn singleplayer_complete_campaign_provider(conf: &crate::persist::config::ConfigImpl) -> SimpleFunc<64, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { + let campaign_details = >::campaign_details(conf); + SimpleFunc::new(move |params, _| { let mut params = params.to_dict(); - // TODO implement serialisation of Campaign[] properly - params.insert(CAMPAIGNS_BYTES_PARAM_KEY, Typed::Bytes(vec![0u8, 0u8, 0u8, 0u8].into())); // first 4 bytes are i32 for length of the rest - params.insert(CAMPAIGNS_WAVES_PARAM_KEY, Typed::HashMap(vec![].into())); - params.insert(CAMPAIGNS_VERSIONS_PARAM_KEY, Typed::HashMap(vec![ - (Typed::Str("CurrentVersionNumber".into()), Typed::Int(0)), - (Typed::Str("LockedCampaignsInfo".into()), Typed::HashMap(vec![ - (Typed::Str("0".into()), Typed::Bool(false.into())) - ].into())), - ].into())); + if let Some(Typed::Str(campaign_id)) = params.get(&CAMPAIGN_ID_PARAM_KEY) { + if let Some(Typed::Int(campaign_difficulty)) = params.get(&CAMPAIGN_DIFFICULTY_PARAM_KEY) { + let complete_campaign = campaign_details.get(&campaign_id.string, campaign_difficulty)?; + params.clear(); + params.insert(CAMPAIGN_WAVES_PARAM_KEY, complete_campaign); + } + } + Ok(params.into()) + }) +} + +const CAMPAIGN_WAVE_NUMBER_PARAM_KEYL: u8 = 73; + +pub(super) fn singleplayer_save_complete_campaign_provider() -> SimpleFunc<68, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { + //let campaign_details = >::campaign_details(conf); + SimpleFunc::new(move |params, user: &crate::UserTy| { + let mut params = params.to_dict(); + if let Some(Typed::Str(campaign_id)) = params.get(&CAMPAIGN_ID_PARAM_KEY) { + if let Some(Typed::Int(campaign_difficulty)) = params.get(&CAMPAIGN_DIFFICULTY_PARAM_KEY) { + if let Some(Typed::Int(wave_number)) = params.get(&CAMPAIGN_WAVE_NUMBER_PARAM_KEYL) { + let user_lock = user.read().unwrap(); + log::info!("User {} completed campaign {} difficulty {} wave {}", user_lock.user()?.token().uuid, campaign_id.string, campaign_difficulty, wave_number); + // TODO save wave as completed + params.clear(); + } + } + } Ok(params.into()) }) } diff --git a/rc_services_room/src/operations/validate_machine.rs b/rc_services_room/src/operations/validate_machine.rs index eec5700..25cd588 100644 --- a/rc_services_room/src/operations/validate_machine.rs +++ b/rc_services_room/src/operations/validate_machine.rs @@ -28,3 +28,19 @@ pub(super) fn validate_robot_provider() -> SimpleFunc<102, crate::UserTy, impl ( Ok(params.into()) }) } + +const CAMPAIGN_ID_PARAM_KEY: u8 = 22; + +pub(super) fn validate_campaign_robot_provider() -> SimpleFunc<59, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result) + Sync + Sync> { + SimpleFunc::new(|params, _user: &crate::UserTy| { + let mut params = params.to_dict(); + if let Some(Typed::Str(campaign_id)) = params.get(&CAMPAIGN_ID_PARAM_KEY) { + log::info!("Got campaign id {}", campaign_id.string); + } + // let lock = user.read().unwrap(); + // let user_info = lock.user()?; + // TODO actually validate the vehicle + params.insert(VALIDATE_ROBOT_RESULT_PARAM_KEY, Typed::Int(ValidateMachineResult::Ok as _)); // this is ignored + Ok(params.into()) + }) +} diff --git a/rc_services_room/src/operations/weapon_order.rs b/rc_services_room/src/operations/weapon_order.rs new file mode 100644 index 0000000..cfdebe1 --- /dev/null +++ b/rc_services_room/src/operations/weapon_order.rs @@ -0,0 +1,14 @@ +use polariton_server::operations::Immediate; + +use crate::persist::config::ConfigProvider; + +pub const DEFAULT_WEAPON_ORDER_PARAM_KEY: u8 = 138; + +pub(super) fn weapon_order_provider(conf: &crate::persist::config::ConfigImpl) -> Immediate<118, crate::UserTy> { + let weapon_orders = conf.weapon_keys(); + Immediate::new(|| { + let mut params = std::collections::HashMap::with_capacity(1); + params.insert(DEFAULT_WEAPON_ORDER_PARAM_KEY, weapon_orders.clone()); + params.into() + }) +} diff --git a/rc_services_room/src/persist/combat.rs b/rc_services_room/src/persist/combat.rs index 3af2115..09d36e1 100644 --- a/rc_services_room/src/persist/combat.rs +++ b/rc_services_room/src/persist/combat.rs @@ -8,6 +8,8 @@ pub struct BattleConfig { pub votes: HashMap>, #[serde(default = "default_game_modes")] pub games: GameModes, + #[serde(default = "default_campaigns")] + pub singleplayer: super::Campaigns, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -129,3 +131,72 @@ fn default_game_modes() -> GameModes { }, } } + +fn default_campaigns() -> super::Campaigns { + super::Campaigns { + campaigns: vec![ + super::Campaign { + id: "strCampaignModeBattle".to_owned(), + excluded_cubes: Vec::default(), + categories: vec![super::ItemCategory::Wheel], + min_cpu: 0, + max_cpu: 2_000, + name: "strCampaignModeBattle".to_owned(), + description: "strCampaignsDesc".to_owned(), + image: "RE_singleplayer_campaign_image_asset_TODO".to_owned(), + rules: Vec::default(), + parameters: Vec::default(), + difficulties: vec![ + super::CampaignDifficulty { + level: 0, + lives: 5, + auto_heal: true, + single_wave_bonus: 1_000, + initial_health_boost: 0.0, + health_boost_wave_increase: 0.0, + initial_damage_boost: 0.0, + damage_boost_wave_increase: 0.0, + } + ], + completed: vec![ + super::CampaignCompletion { + wave: 0, + difficulty: false, + } + ], + map: "RC_Planet_Neptune_03_BA".to_owned(), + campaign_type: super::CampaignType::Elimination, + waves: vec![ + super::Wave { + player_spawn_location: 0, + robots_in_wave: vec![ + super::WaveRobot { + name: "strCampaignAnimalName".to_owned(), + weapon: "strT5PlasmaGoldenName".to_owned(), + movement: "strT5SteeringWheelGoldenName".to_owned(), + rank: "strT0".to_owned(), + count: 5, + robot_data: super::VALID_ROBOT.into(), + colour_data: super::VALID_COLOUR.into(), + time_to_spawn: 1, + kills_to_spawn: 0, + time_to_despawn: 60, + kills_to_despawn: 1, + initial_robot_amount: 0, + periodic_robot_amount: 3, + spawn_interval: 1, + min_robot_amount: 1, + max_robot_amount: 5, + is_boss: false, + is_kill_requirement: true, + } + ], + kill_target: 1, + time_min: 1, + time_max: 1 * 60, + } + ], + } + ] + } +} diff --git a/rc_services_room/src/persist/config/cubes_json.rs b/rc_services_room/src/persist/config/cubes_json.rs index 69958e7..6bd5911 100644 --- a/rc_services_room/src/persist/config/cubes_json.rs +++ b/rc_services_room/src/persist/config/cubes_json.rs @@ -125,6 +125,18 @@ impl super::ConfigProvider for CubeConfig { Typed::ObjArr(weapon_upgrades.into()) } + fn weapon_keys(&self) -> Typed { + let mut seen_keys = std::collections::HashSet::new(); + for cube in self.cubes.values() { + if cube.weapon.is_some() { + let key = crate::data::cube_list::item_key(cube.info.category.into(), cube.info.size.into()); + seen_keys.insert(key); + } + } + let keys_vec: Vec = seen_keys.into_iter().collect(); + Typed::IntArr(keys_vec.into()) + } + fn tech_tree_nodes(&self, unlocked_cubes: &std::collections::HashSet) -> Typed { let mut seen_cubes = std::collections::HashSet::with_capacity(self.cubes.len()); let mut needed_cubes = std::collections::HashSet::with_capacity(self.cubes.len()); @@ -182,4 +194,41 @@ impl super::ConfigProvider for CubeConfig { let game_mode_data: crate::data::game_mode::GameModeConfigs = self.battle.games.into(); game_mode_data.as_transmissible() } + + fn campaigns_parameters(&self) -> Typed { + self.battle.singleplayer.clone().into_campaign_params().as_transmissible() + } + + fn campaign_waves(&self) -> Typed { + self.battle.singleplayer.clone().into_waves().as_transmissible() + } + + fn campaign_version(&self) -> Typed { + let mut locked_map = std::collections::HashMap::with_capacity(self.battle.singleplayer.campaigns.len()); + for campaign in self.battle.singleplayer.campaigns.iter() { + locked_map.insert(campaign.id.clone(), true); + } + crate::data::campaign::GameModeVersionParameters { + current_version: 0, + is_locked: locked_map, + }.as_transmissible() + } + + fn campaign_details(&self) -> super::CompleteCampaignProvider { + let mut map = std::collections::HashMap::with_capacity(self.battle.singleplayer.campaigns.len()); + for campaign in self.battle.singleplayer.campaigns.iter() { + //let waves_data: Vec = campaign.waves.iter().map(|x| x.clone().into()).collect(); + let mut difficulty_map = std::collections::HashMap::with_capacity(campaign.difficulties.len()); + for difficulty in campaign.difficulties.iter() { + let difficulty_data: crate::data::campaign::CampaignDifficultyData = difficulty.clone().into(); + let complete_campaign = crate::data::campaign::CampaignWavesDifficultyData { + difficulty: difficulty_data, + waves: campaign.waves.iter().map(|x| x.clone().into()).collect(), + }; + difficulty_map.insert(difficulty.level, complete_campaign); + } + map.insert(campaign.id.clone(), difficulty_map); + } + super::CompleteCampaignProvider::new(map) + } } diff --git a/rc_services_room/src/persist/config/mod.rs b/rc_services_room/src/persist/config/mod.rs index 769ca75..782c863 100644 --- a/rc_services_room/src/persist/config/mod.rs +++ b/rc_services_room/src/persist/config/mod.rs @@ -2,7 +2,7 @@ mod cubes_json; pub use cubes_json::CubeConfig; mod traits; -pub use traits::ConfigProvider; +pub use traits::{ConfigProvider, CompleteCampaignProvider}; pub type ConfigImpl = CubeConfig; diff --git a/rc_services_room/src/persist/config/traits.rs b/rc_services_room/src/persist/config/traits.rs index 110c2b6..64aa492 100644 --- a/rc_services_room/src/persist/config/traits.rs +++ b/rc_services_room/src/persist/config/traits.rs @@ -5,9 +5,38 @@ pub trait ConfigProvider { fn movement_list(&self) -> Typed; fn weapon_list(&self) -> Typed; fn weapon_upgrade_list(&self) -> Typed; + fn weapon_keys(&self) -> Typed; fn tech_tree_nodes(&self, unlocked_cubes: &std::collections::HashSet) -> Typed; fn ids(&self) -> Vec; fn regen_config(&self) -> Typed; fn after_battle_vote_config(&self) -> Typed; fn game_mode_config(&self) -> Typed; + fn campaigns_parameters(&self) -> Typed; + fn campaign_waves(&self) -> Typed; + fn campaign_version(&self) -> Typed; + fn campaign_details(&self) -> CompleteCampaignProvider; +} + +pub struct CompleteCampaignProvider { + map: std::collections::HashMap>, +} + +impl CompleteCampaignProvider { + pub fn new(map: std::collections::HashMap>) -> Self { + Self { map } + } + + pub fn get(&self, id: &str, difficulty: &i32) -> Result, i16> { + if let Some(campaign) = self.map.get(id) { + if let Some(details) = campaign.get(difficulty) { + Ok(details.as_transmissible()) + } else { + log::warn!("Couldn't find difficulty {} in campaign `{}`", difficulty, id); + Err(crate::data::error_codes::WebServicesError::DatabaseError as i16) + } + } else { + log::warn!("Couldn't find campaign {} (ignoring difficulty {})", id, difficulty); + Err(crate::data::error_codes::WebServicesError::DatabaseError as i16) + } + } } diff --git a/rc_services_room/src/persist/mod.rs b/rc_services_room/src/persist/mod.rs index a9bbaaa..23485b4 100644 --- a/rc_services_room/src/persist/mod.rs +++ b/rc_services_room/src/persist/mod.rs @@ -19,3 +19,786 @@ pub use tech_tree::TechTreeData; mod combat; pub use combat::BattleConfig; + +mod singleplayer; +pub use singleplayer::{Campaigns, Campaign, CampaignDifficulty, CampaignCompletion, CampaignType, Wave, WaveRobot}; + +// TODO put this in core lib + +pub(self) const VALID_ROBOT: &[u8] = &[64, + 0, + 0, + 0, + 38, + 190, + 25, + 77, + 24, + 6, + 29, + 0, + 80, + 135, + 103, + 211, + 27, + 4, + 27, + 6, + 80, + 135, + 103, + 211, + 21, + 4, + 27, + 12, + 198, + 224, + 138, + 13, + 24, + 4, + 26, + 0, + 198, + 224, + 138, + 13, + 24, + 4, + 27, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 28, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 25, + 0, + 198, + 224, + 138, + 13, + 24, + 4, + 29, + 0, + 198, + 224, + 138, + 13, + 23, + 4, + 29, + 0, + 198, + 224, + 138, + 13, + 23, + 4, + 28, + 0, + 198, + 224, + 138, + 13, + 23, + 4, + 27, + 0, + 198, + 224, + 138, + 13, + 23, + 4, + 26, + 22, + 198, + 224, + 138, + 13, + 23, + 4, + 25, + 22, + 198, + 224, + 138, + 13, + 25, + 4, + 29, + 0, + 198, + 224, + 138, + 13, + 25, + 4, + 27, + 22, + 198, + 224, + 138, + 13, + 25, + 4, + 28, + 22, + 198, + 224, + 138, + 13, + 25, + 4, + 26, + 22, + 198, + 224, + 138, + 13, + 25, + 4, + 25, + 22, + 198, + 224, + 138, + 13, + 26, + 4, + 29, + 0, + 198, + 224, + 138, + 13, + 26, + 4, + 28, + 22, + 198, + 224, + 138, + 13, + 26, + 4, + 27, + 22, + 198, + 224, + 138, + 13, + 26, + 4, + 26, + 22, + 198, + 224, + 138, + 13, + 26, + 4, + 25, + 22, + 198, + 224, + 138, + 13, + 22, + 4, + 25, + 0, + 198, + 224, + 138, + 13, + 22, + 4, + 26, + 0, + 198, + 224, + 138, + 13, + 22, + 4, + 27, + 23, + 198, + 224, + 138, + 13, + 22, + 4, + 28, + 23, + 198, + 224, + 138, + 13, + 22, + 4, + 29, + 0, + 198, + 224, + 138, + 13, + 25, + 5, + 29, + 0, + 198, + 224, + 138, + 13, + 23, + 5, + 29, + 0, + 198, + 224, + 138, + 13, + 24, + 5, + 29, + 0, + 198, + 224, + 138, + 13, + 22, + 5, + 28, + 0, + 198, + 224, + 138, + 13, + 26, + 5, + 28, + 0, + 198, + 224, + 138, + 13, + 25, + 5, + 26, + 0, + 198, + 224, + 138, + 13, + 23, + 5, + 26, + 0, + 198, + 224, + 138, + 13, + 24, + 4, + 24, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 23, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 22, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 21, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 20, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 19, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 18, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 17, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 16, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 15, + 22, + 198, + 224, + 138, + 13, + 24, + 4, + 14, + 22, + 198, + 224, + 138, + 13, + 23, + 4, + 17, + 12, + 198, + 224, + 138, + 13, + 25, + 4, + 17, + 6, + 198, + 224, + 138, + 13, + 23, + 4, + 16, + 12, + 198, + 224, + 138, + 13, + 25, + 4, + 16, + 6, + 198, + 224, + 138, + 13, + 23, + 4, + 15, + 12, + 198, + 224, + 138, + 13, + 25, + 4, + 15, + 6, + 198, + 224, + 138, + 13, + 23, + 4, + 14, + 12, + 198, + 224, + 138, + 13, + 25, + 4, + 14, + 6, + 198, + 224, + 138, + 13, + 22, + 4, + 17, + 12, + 198, + 224, + 138, + 13, + 26, + 4, + 17, + 6, + 198, + 224, + 138, + 13, + 22, + 4, + 16, + 12, + 198, + 224, + 138, + 13, + 26, + 4, + 16, + 6, + 198, + 224, + 138, + 13, + 22, + 4, + 15, + 12, + 198, + 224, + 138, + 13, + 26, + 4, + 15, + 6, + 198, + 224, + 138, + 13, + 22, + 4, + 14, + 12, + 198, + 224, + 138, + 13, + 26, + 4, + 14, + 6, + 240, + 75, + 110, + 137, + 21, + 4, + 15, + 12, + 240, + 75, + 110, + 137, + 27, + 4, + 15, + 6]; + +pub(self) const VALID_COLOUR: &[u8] = &[64, + 0, + 0, + 0, + 0, + 24, + 6, + 29, + 0, + 27, + 4, + 27, + 0, + 21, + 4, + 27, + 0, + 24, + 4, + 26, + 0, + 24, + 4, + 27, + 0, + 24, + 4, + 28, + 0, + 24, + 4, + 25, + 0, + 24, + 4, + 29, + 0, + 23, + 4, + 29, + 0, + 23, + 4, + 28, + 0, + 23, + 4, + 27, + 0, + 23, + 4, + 26, + 0, + 23, + 4, + 25, + 0, + 25, + 4, + 29, + 0, + 25, + 4, + 27, + 0, + 25, + 4, + 28, + 0, + 25, + 4, + 26, + 0, + 25, + 4, + 25, + 0, + 26, + 4, + 29, + 0, + 26, + 4, + 28, + 0, + 26, + 4, + 27, + 0, + 26, + 4, + 26, + 0, + 26, + 4, + 25, + 0, + 22, + 4, + 25, + 0, + 22, + 4, + 26, + 0, + 22, + 4, + 27, + 0, + 22, + 4, + 28, + 0, + 22, + 4, + 29, + 1, + 25, + 5, + 29, + 1, + 23, + 5, + 29, + 1, + 24, + 5, + 29, + 1, + 22, + 5, + 28, + 1, + 26, + 5, + 28, + 1, + 25, + 5, + 26, + 1, + 23, + 5, + 26, + 0, + 24, + 4, + 24, + 0, + 24, + 4, + 23, + 0, + 24, + 4, + 22, + 0, + 24, + 4, + 21, + 0, + 24, + 4, + 20, + 0, + 24, + 4, + 19, + 0, + 24, + 4, + 18, + 0, + 24, + 4, + 17, + 0, + 24, + 4, + 16, + 0, + 24, + 4, + 15, + 0, + 24, + 4, + 14, + 0, + 23, + 4, + 17, + 0, + 25, + 4, + 17, + 0, + 23, + 4, + 16, + 0, + 25, + 4, + 16, + 0, + 23, + 4, + 15, + 0, + 25, + 4, + 15, + 0, + 23, + 4, + 14, + 0, + 25, + 4, + 14, + 0, + 22, + 4, + 17, + 0, + 26, + 4, + 17, + 0, + 22, + 4, + 16, + 0, + 26, + 4, + 16, + 0, + 22, + 4, + 15, + 0, + 26, + 4, + 15, + 0, + 22, + 4, + 14, + 0, + 26, + 4, + 14, + 0, + 21, + 4, + 15, + 0, + 27, + 4, + 15]; diff --git a/rc_services_room/src/persist/singleplayer.rs b/rc_services_room/src/persist/singleplayer.rs new file mode 100644 index 0000000..14a93a0 --- /dev/null +++ b/rc_services_room/src/persist/singleplayer.rs @@ -0,0 +1,224 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Campaigns { + pub campaigns: Vec, +} + +impl Campaigns { + pub fn into_campaign_params(self) -> crate::data::campaign::CampaignsGameParameters { + crate::data::campaign::CampaignsGameParameters { campaigns: self.campaigns.into_iter().map(|x| x.into_campaign_params()).collect() } + } + + pub fn into_waves(self) -> crate::data::campaign::LiveCampaignWaves { + crate::data::campaign::LiveCampaignWaves { waves: self.campaigns.into_iter().map(|x| x.into_waves()).collect() } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Campaign { + pub id: String, + pub excluded_cubes: Vec, // encoded to hex strings + pub categories: Vec, + pub min_cpu: i32, + pub max_cpu: i32, + pub name: String, + pub description: String, + pub image: String, + pub rules: Vec, + pub parameters: Vec>, + pub difficulties: Vec, + pub completed: Vec, + pub map: String, + pub campaign_type: CampaignType, + pub waves: Vec, +} + +impl Campaign { + pub fn into_campaign_params(self) -> crate::data::campaign::CampaignParameters { + crate::data::campaign::CampaignParameters { + id: self.id, + excluded_cubes: self.excluded_cubes, + categories: self.categories.into_iter().map(|x| x.into()).collect(), + min_cpu: self.min_cpu, + max_cpu: self.max_cpu, + name: self.name, + description: self.description, + image: self.image, + rules: self.rules, + parameters: self.parameters, + difficulties: self.difficulties.into_iter().map(|x| x.into()).collect(), + completed: self.completed.into_iter().enumerate().map(|(i, x)| x.into_data(i as _)).collect(), + map: self.map, + } + } + + pub fn into_waves(self) -> crate::data::campaign::WavesData { + crate::data::campaign::WavesData { + id: self.id, + waves: self.waves.into_iter().map(|x| x.into()).collect(), + campaign_type: self.campaign_type.into(), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct CampaignDifficulty { + pub level: i32, + pub lives: i32, + pub auto_heal: bool, + pub single_wave_bonus: i32, + pub initial_health_boost: f32, + pub health_boost_wave_increase: f32, + pub initial_damage_boost: f32, + pub damage_boost_wave_increase: f32, +} + +impl std::convert::Into for CampaignDifficulty { + fn into(self) -> crate::data::campaign::CampaignDifficultyData { + crate::data::campaign::CampaignDifficultyData { + level: self.level, + lives: self.lives, + auto_heal: self.auto_heal, + single_wave_bonus: self.single_wave_bonus, + initial_health_boost: self.initial_health_boost, + health_boost_wave_increase: self.health_boost_wave_increase, + initial_damage_boost: self.initial_damage_boost, + damage_boost_wave_increase: self.damage_boost_wave_increase, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct CampaignCompletion { + pub wave: i32, + pub difficulty: bool, +} + +impl CampaignCompletion { + pub fn into_data(self, index: i32) -> crate::data::campaign::CampaignCompletionData { + crate::data::campaign::CampaignCompletionData { + index, + wave: self.wave, + difficulty: self.difficulty, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Wave { + #[serde(default)] + pub player_spawn_location: i32, + pub robots_in_wave: Vec, + pub kill_target: i32, + #[serde(default)] + pub time_min: i32, + pub time_max: i32, +} + +impl std::convert::Into for Wave { + fn into(self) -> crate::data::campaign::WaveData { + crate::data::campaign::WaveData { + robots_in_wave: self.robots_in_wave.into_iter().map(|x| x.into()).collect(), + } + } +} + +impl std::convert::Into for Wave { + fn into(self) -> crate::data::campaign::CompleteWaveData { + crate::data::campaign::CompleteWaveData { + player_spawn_location: self.player_spawn_location, + robots_in_wave: self.robots_in_wave.into_iter().map(|x| x.into()).collect(), + kill_target: self.kill_target, + time_min: self.time_min, + time_max: self.time_max, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct WaveRobot { // times appear to be in seconds + pub name: String, + pub weapon: String, + pub movement: String, + pub rank: String, + pub count: i32, + pub robot_data: Vec, + pub colour_data: Vec, + #[serde(default)] + pub time_to_spawn: i32, + #[serde(default)] + pub kills_to_spawn: i32, + #[serde(default)] + pub time_to_despawn: i32, + #[serde(default)] + pub kills_to_despawn: i32, + #[serde(default = "default_1")] + pub initial_robot_amount: i32, + #[serde(default)] + pub periodic_robot_amount: i32, + #[serde(default = "default_1")] + pub spawn_interval: i32, + #[serde(default = "default_1")] + pub min_robot_amount: i32, + #[serde(default)] + pub max_robot_amount: i32, + #[serde(default)] + pub is_boss: bool, + #[serde(default)] + pub is_kill_requirement: bool, +} + +fn default_1() -> i32 { + 1 +} + +impl std::convert::Into for WaveRobot { + fn into(self) -> crate::data::campaign::WaveRobotData { + crate::data::campaign::WaveRobotData { + name: self.name, + weapon: self.weapon, + movement: self.movement, + rank: self.rank, + count: self.count, + } + } +} + +impl std::convert::Into for WaveRobot { + fn into(self) -> crate::data::campaign::CompleteWaveRobotData { + crate::data::campaign::CompleteWaveRobotData { + name: self.name, + robot_data: self.robot_data, + colour_data: self.colour_data, + time_to_spawn: self.time_to_spawn, + kills_to_spawn: self.kills_to_spawn, + time_to_despawn: self.time_to_despawn, + kills_to_despawn: self.kills_to_despawn, + initial_robot_amount: self.initial_robot_amount, + periodic_robot_amount: self.periodic_robot_amount, + spawn_interval: self.spawn_interval, + min_robot_amount: self.min_robot_amount, + max_robot_amount: self.max_robot_amount, + is_boss: self.is_boss, + is_kill_requirement: self.is_kill_requirement, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, Copy)] +pub enum CampaignType { + TimedElimination = 0, + Survival = 1, + Elimination = 2, +} + +impl std::convert::Into for CampaignType { + fn into(self) -> crate::data::campaign::CampaignType { + match self { + Self::TimedElimination => crate::data::campaign::CampaignType::TimedElimination, + Self::Survival => crate::data::campaign::CampaignType::Survival, + Self::Elimination => crate::data::campaign::CampaignType::Elimination, + } + } +} diff --git a/rc_services_room/src/persist/weapon.rs b/rc_services_room/src/persist/weapon.rs index 3a440a3..67ac728 100644 --- a/rc_services_room/src/persist/weapon.rs +++ b/rc_services_room/src/persist/weapon.rs @@ -10,18 +10,28 @@ pub struct WeaponData { pub base_inaccuracy: Option, pub base_air_inaccuracy: Option, pub movement_inaccuracy: Option, + #[serde(alias="movement_max_threshold_speed")] pub movement_max_speed: Option, + #[serde(alias="movement_min_threshold_speed")] pub movement_min_speed: Option, + #[serde(alias="gun_rotation_threshold_slow")] pub gun_rotation_slow: Option, + #[serde(alias="movement_inaccuracy_decay_time")] pub movement_inaccuracy_decay: Option, + #[serde(alias="slow_rotation_inaccuracy_decay_time")] pub slow_rotation_decay: Option, + #[serde(alias="quick_rotation_inaccuracy_decay_time")] pub quick_rotation_decay: Option, + #[serde(alias="movement_inaccuracy_recovery_time")] pub movement_inaccuracy_recovery: Option, pub repeat_fire_inaccuracy_total_degrees: Option, + #[serde(alias="repeat_fire_inaccuracy_decay_time")] pub repeat_fire_inaccuracy_decay: Option, + #[serde(alias="repeat_fire_inaccuracy_recovery_time")] pub repeat_fire_innaccuracy_recovery: Option, pub fire_instant_accuracy_decay: Option, // degrees pub accuracy_non_recover_time: Option, + #[serde(alias="accuracy_decay_time")] pub accuracy_decay: Option, pub damage_radius: Option, pub plasma_time_to_full_damage: Option, @@ -36,9 +46,12 @@ pub struct WeaponData { pub aeroflak_ground_clearance: Option, pub aeroflak_max_stacks: Option, pub aeroflak_damage_per_stack: Option, + #[serde(alias="aeroflak_buff_time_to_expire")] pub aeroflak_stack_expire: Option, + #[serde(alias="cooldown_between_shots")] pub shot_cooldown: Option, pub smart_rotation_cooldown: Option, + #[serde(alias="smart_rotation_extra_cooldown_time")] pub smart_rotation_cooldown_extra: Option, pub smart_rotation_max_stacks: Option, pub spin_up_time: Option, diff --git a/utils/cube_gen.py b/utils/cube_gen.py index 67ad50e..780a544 100644 --- a/utils/cube_gen.py +++ b/utils/cube_gen.py @@ -426,7 +426,7 @@ def main(asset_in, cubes=None, weapons=None, movement=None): "votes_required": 2, } ], - } + }, } } last_tech_tree_id = 0