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

Get campaign mode working, fix health/damage config

This commit is contained in:
NGnius (Graham)
2025-03-29 22:08:08 -04:00
parent 78941fb8ff
commit 610f54e236
23 changed files with 1601 additions and 56 deletions

View File

@@ -0,0 +1,301 @@
use polariton::operation::Typed;
pub struct CampaignsGameParameters {
pub campaigns: Vec<CampaignParameters>,
}
impl CampaignsGameParameters {
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
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<C>(&self) -> Typed<C> {
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<u32>, // encoded to hex strings
pub categories: Vec<super::weapon_list::ItemCategory>,
pub min_cpu: i32,
pub max_cpu: i32,
pub name: String,
pub description: String,
pub image: String,
pub rules: Vec<String>,
pub parameters: Vec<Vec<String>>,
pub difficulties: Vec<CampaignDifficultyData>,
pub completed: Vec<CampaignCompletionData>,
pub map: String,
}
impl CampaignParameters {
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
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<usize> {
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<usize> {
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<WavesData>,
}
impl LiveCampaignWaves {
pub fn as_transmissible<C>(&self) -> Typed<C> {
Typed::HashMap(self.waves.iter().flat_map(|waves| waves.as_transmissible_key_val()).collect::<Vec<_>>().into())
}
}
pub struct WavesData {
pub id: String,
pub waves: Vec<WaveData>,
pub campaign_type: CampaignType,
}
impl WavesData {
pub fn as_transmissible_key_val<C>(&self) -> [(Typed<C>, Typed<C>); 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::<Vec<_>>().into())),
(Typed::Str(format!("campaignType_{}", self.id).into()), Typed::Int(self.campaign_type as _)),
]
}
}
pub struct WaveData {
pub robots_in_wave: Vec<WaveRobotData>,
}
impl WaveData {
pub fn as_transmissible_key_val<C>(&self, index: usize) -> [(Typed<C>, Typed<C>); 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::<Vec<_>>().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<C>(&self) -> Typed<C> {
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<String, bool>,
}
impl GameModeVersionParameters {
pub fn as_transmissible<C>(&self) -> Typed<C> {
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::<Vec<_>>().into())),
].into())
}
}
pub struct CampaignWavesDifficultyData {
pub difficulty: CampaignDifficultyData,
pub waves: Vec<CompleteWaveData>,
}
impl CampaignWavesDifficultyData {
pub fn as_transmissible<C>(&self) -> Typed<C> {
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<usize> {
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<CompleteWaveRobotData>,
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<usize> {
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<u8>,
pub colour_data: Vec<u8>,
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<usize> {
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)
}
}

View File

@@ -127,3 +127,7 @@ impl ItemType {
}
}
}
pub fn item_key(category: super::weapon_list::ItemCategory, tier: ItemTier) -> i32 {
category.but_bigger() + (tier as i32)
}

View File

@@ -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<u8> {
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()
}

View File

@@ -14,6 +14,7 @@ pub struct ScoreMultipliersData {
impl ScoreMultipliersData {
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
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<C>(&self) -> Typed<C> {

View File

@@ -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(),

View File

@@ -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())

View File

@@ -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())
})

View File

@@ -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())
],

View File

@@ -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<ParameterTable, i16>) + 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())
})
}

View File

@@ -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<crate::UserTy>
.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<crate::UserTy>
.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<crate::UserTy>
.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
}

View File

@@ -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())

View File

@@ -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())
})
}

View File

@@ -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<ParameterTable, i16>) + 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<ParameterTable, i16>) + Sync + Sync> {
let campaign_details = <crate::persist::config::ConfigImpl as crate::persist::config::ConfigProvider<()>>::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<ParameterTable, i16>) + Sync + Sync> {
//let campaign_details = <crate::persist::config::ConfigImpl as crate::persist::config::ConfigProvider<()>>::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())
})
}

View File

@@ -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<ParameterTable, i16>) + 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())
})
}

View File

@@ -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()
})
}

View File

@@ -8,6 +8,8 @@ pub struct BattleConfig {
pub votes: HashMap<Vote, Vec<VoteThreshold>>,
#[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,
}
],
}
]
}
}

View File

@@ -125,6 +125,18 @@ impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
Typed::ObjArr(weapon_upgrades.into())
}
fn weapon_keys(&self) -> Typed<C> {
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<i32> = seen_keys.into_iter().collect();
Typed::IntArr(keys_vec.into())
}
fn tech_tree_nodes(&self, unlocked_cubes: &std::collections::HashSet<u32>) -> Typed<C> {
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 <C: Clone> super::ConfigProvider<C> 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<C> {
self.battle.singleplayer.clone().into_campaign_params().as_transmissible()
}
fn campaign_waves(&self) -> Typed<C> {
self.battle.singleplayer.clone().into_waves().as_transmissible()
}
fn campaign_version(&self) -> Typed<C> {
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<crate::data::campaign::CompleteWaveData> = 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)
}
}

View File

@@ -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;

View File

@@ -5,9 +5,38 @@ pub trait ConfigProvider<C> {
fn movement_list(&self) -> Typed<C>;
fn weapon_list(&self) -> Typed<C>;
fn weapon_upgrade_list(&self) -> Typed<C>;
fn weapon_keys(&self) -> Typed<C>;
fn tech_tree_nodes(&self, unlocked_cubes: &std::collections::HashSet<u32>) -> Typed<C>;
fn ids(&self) -> Vec<u32>;
fn regen_config(&self) -> Typed<C>;
fn after_battle_vote_config(&self) -> Typed<C>;
fn game_mode_config(&self) -> Typed<C>;
fn campaigns_parameters(&self) -> Typed<C>;
fn campaign_waves(&self) -> Typed<C>;
fn campaign_version(&self) -> Typed<C>;
fn campaign_details(&self) -> CompleteCampaignProvider;
}
pub struct CompleteCampaignProvider {
map: std::collections::HashMap<String, std::collections::HashMap<i32, crate::data::campaign::CampaignWavesDifficultyData>>,
}
impl CompleteCampaignProvider {
pub fn new(map: std::collections::HashMap<String, std::collections::HashMap<i32, crate::data::campaign::CampaignWavesDifficultyData>>) -> Self {
Self { map }
}
pub fn get<C>(&self, id: &str, difficulty: &i32) -> Result<Typed<C>, 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)
}
}
}

View File

@@ -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];

View File

@@ -0,0 +1,224 @@
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Campaigns {
pub campaigns: Vec<Campaign>,
}
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<u32>, // encoded to hex strings
pub categories: Vec<super::ItemCategory>,
pub min_cpu: i32,
pub max_cpu: i32,
pub name: String,
pub description: String,
pub image: String,
pub rules: Vec<String>,
pub parameters: Vec<Vec<String>>,
pub difficulties: Vec<CampaignDifficulty>,
pub completed: Vec<CampaignCompletion>,
pub map: String,
pub campaign_type: CampaignType,
pub waves: Vec<Wave>,
}
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<crate::data::campaign::CampaignDifficultyData> 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<WaveRobot>,
pub kill_target: i32,
#[serde(default)]
pub time_min: i32,
pub time_max: i32,
}
impl std::convert::Into<crate::data::campaign::WaveData> 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<crate::data::campaign::CompleteWaveData> 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<u8>,
pub colour_data: Vec<u8>,
#[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<crate::data::campaign::WaveRobotData> 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<crate::data::campaign::CompleteWaveRobotData> 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<crate::data::campaign::CampaignType> 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,
}
}
}

View File

@@ -10,18 +10,28 @@ pub struct WeaponData {
pub base_inaccuracy: Option<f32>,
pub base_air_inaccuracy: Option<f32>,
pub movement_inaccuracy: Option<f32>,
#[serde(alias="movement_max_threshold_speed")]
pub movement_max_speed: Option<f32>,
#[serde(alias="movement_min_threshold_speed")]
pub movement_min_speed: Option<f32>,
#[serde(alias="gun_rotation_threshold_slow")]
pub gun_rotation_slow: Option<f32>,
#[serde(alias="movement_inaccuracy_decay_time")]
pub movement_inaccuracy_decay: Option<f32>,
#[serde(alias="slow_rotation_inaccuracy_decay_time")]
pub slow_rotation_decay: Option<f32>,
#[serde(alias="quick_rotation_inaccuracy_decay_time")]
pub quick_rotation_decay: Option<f32>,
#[serde(alias="movement_inaccuracy_recovery_time")]
pub movement_inaccuracy_recovery: Option<f32>,
pub repeat_fire_inaccuracy_total_degrees: Option<f32>,
#[serde(alias="repeat_fire_inaccuracy_decay_time")]
pub repeat_fire_inaccuracy_decay: Option<f32>,
#[serde(alias="repeat_fire_inaccuracy_recovery_time")]
pub repeat_fire_innaccuracy_recovery: Option<f32>,
pub fire_instant_accuracy_decay: Option<f32>, // degrees
pub accuracy_non_recover_time: Option<f32>,
#[serde(alias="accuracy_decay_time")]
pub accuracy_decay: Option<f32>,
pub damage_radius: Option<f32>,
pub plasma_time_to_full_damage: Option<f32>,
@@ -36,9 +46,12 @@ pub struct WeaponData {
pub aeroflak_ground_clearance: Option<f32>,
pub aeroflak_max_stacks: Option<i32>,
pub aeroflak_damage_per_stack: Option<i32>,
#[serde(alias="aeroflak_buff_time_to_expire")]
pub aeroflak_stack_expire: Option<f32>,
#[serde(alias="cooldown_between_shots")]
pub shot_cooldown: Option<f32>,
pub smart_rotation_cooldown: Option<f32>,
#[serde(alias="smart_rotation_extra_cooldown_time")]
pub smart_rotation_cooldown_extra: Option<f32>,
pub smart_rotation_max_stacks: Option<f32>,
pub spin_up_time: Option<f32>,

View File

@@ -426,7 +426,7 @@ def main(asset_in, cubes=None, weapons=None, movement=None):
"votes_required": 2,
}
],
}
},
}
}
last_tech_tree_id = 0