mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Move common components to shared lib
This commit is contained in:
14
rc_core/Cargo.toml
Normal file
14
rc_core/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "rc_core"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
log.workspace = true
|
||||
polariton.workspace = true
|
||||
hex = "0.4"
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
chrono = "0.4"
|
||||
polariton_server.workspace = true
|
||||
tokio = { version = "1.43", features = [ "net", "macros", "rt-multi-thread", "io-util", "time"] }
|
||||
19
rc_core/src/data/auto_regen.rs
Normal file
19
rc_core/src/data/auto_regen.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use polariton::operation::Typed;
|
||||
|
||||
pub struct AutoRegenHealthConfig {
|
||||
pub seconds_to_wait_for_heal: f32,
|
||||
pub seconds_to_full_heal: f32,
|
||||
pub threshold_to_start_sound: f32,
|
||||
pub enable_auto_heal: bool,
|
||||
}
|
||||
|
||||
impl AutoRegenHealthConfig {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
let mut bytes = Vec::with_capacity(13);
|
||||
bytes.extend_from_slice(&self.seconds_to_wait_for_heal.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.seconds_to_full_heal.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.threshold_to_start_sound.to_le_bytes());
|
||||
bytes.push(self.enable_auto_heal as u8);
|
||||
Typed::Bytes(bytes.into())
|
||||
}
|
||||
}
|
||||
301
rc_core/src/data/campaign.rs
Normal file
301
rc_core/src/data/campaign.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
133
rc_core/src/data/cube_list.rs
Normal file
133
rc_core/src/data/cube_list.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
#![allow(dead_code)]
|
||||
use std::collections::HashMap;
|
||||
use polariton::operation::{Dict, Typed};
|
||||
|
||||
pub struct CubeInfo<C: Clone> {
|
||||
pub cpu: u32,
|
||||
pub health: u32,
|
||||
pub health_boost: f32,
|
||||
pub grey_out_in_tutorial: bool,
|
||||
pub visibility: VisibilityMode,
|
||||
pub indestructible: bool,
|
||||
pub category: super::weapon_list::ItemCategory,
|
||||
pub placements: u32, // default 63
|
||||
pub protonium: bool,
|
||||
pub unlocked_by_league: bool,
|
||||
pub league_unlock_index: i32,
|
||||
pub stats: HashMap<String, Typed<C>>,
|
||||
pub description: String,
|
||||
pub size: ItemTier,
|
||||
pub type_: ItemType,
|
||||
pub ranking: i32,
|
||||
pub cosmetic: bool,
|
||||
pub variant_of: String, // cube id (in hex)
|
||||
pub ignore_in_weapon_list: bool,
|
||||
}
|
||||
|
||||
impl <C: Clone> CubeInfo<C> {
|
||||
pub fn as_transmissible(&self) -> Typed<C> {
|
||||
Typed::HashMap(vec![
|
||||
(Typed::Str("cpuRating".into()), Typed::Int(self.cpu as i32)),
|
||||
(Typed::Str("health".into()), Typed::Int(self.health as i32)),
|
||||
(Typed::Str("healthBoost".into()), Typed::Float(self.health_boost)),
|
||||
(Typed::Str("GreyOutInTutorial".into()), Typed::Bool(self.grey_out_in_tutorial.into())),
|
||||
(Typed::Str("buildVisibility".into()), Typed::Str(self.visibility.as_str().into())),
|
||||
(Typed::Str("isIndestructible".into()), Typed::Bool(self.indestructible.into())),
|
||||
(Typed::Str("ItemCategory".into()), Typed::Int(self.category as i32)),
|
||||
(Typed::Str("PlacementFaces".into()), Typed::Int(self.placements as i32)),
|
||||
(Typed::Str("protoniumCrystal".into()), Typed::Bool(self.protonium.into())),
|
||||
(Typed::Str("UnlockedByLeague".into()), Typed::Bool(self.unlocked_by_league.into())),
|
||||
(Typed::Str("LeagueUnlockIndex".into()), Typed::Int(self.league_unlock_index)),
|
||||
(Typed::Str("DisplayStats".into()), {
|
||||
let items: Vec<(Typed<C>, Typed<C>)> = self.stats.iter().map(|(key, val)| (Typed::<C>::Str(key.into()), val.to_owned())).collect();
|
||||
//Typed::HashMap(items.into())
|
||||
Typed::Dict(Dict {
|
||||
key_ty: polariton::serdes::TypePrefix::Str,
|
||||
val_ty: polariton::serdes::TypePrefix::Any,
|
||||
items,
|
||||
})
|
||||
}),
|
||||
(Typed::Str("Description".into()), Typed::Str(self.description.clone().into())),
|
||||
(Typed::Str("ItemSize".into()), Typed::Int(self.size as i32)),
|
||||
(Typed::Str("ItemType".into()), Typed::Str(self.type_.as_str().into())),
|
||||
(Typed::Str("robotRanking".into()), Typed::Int(self.ranking)),
|
||||
(Typed::Str("isCosmetic".into()), Typed::Bool(self.cosmetic.into())),
|
||||
(Typed::Str("variantOf".into()), Typed::Str(self.variant_of.clone().into())),
|
||||
(Typed::Str("ignoreInWeaponsList".into()), Typed::Bool(self.ignore_in_weapon_list.into())), // optional
|
||||
].into())
|
||||
}
|
||||
|
||||
pub fn as_transmissible_key_val(&self, cube_id: u32) -> (Typed<C>, Typed<C>) {
|
||||
(Typed::Str(hex::encode(cube_id.to_be_bytes()).into()), self.as_transmissible())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum VisibilityMode {
|
||||
Mothership,
|
||||
All,
|
||||
Tutorial,
|
||||
None,
|
||||
}
|
||||
|
||||
impl VisibilityMode {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Mothership => "Mothership",
|
||||
Self::All => "All",
|
||||
Self::Tutorial => "Tutorial",
|
||||
Self::None => "None",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ItemTier {
|
||||
NoTier = 0,
|
||||
T0 = 100,
|
||||
T1 = 200,
|
||||
T2 = 300,
|
||||
T3 = 400,
|
||||
T4 = 500,
|
||||
T5 = 600,
|
||||
}
|
||||
|
||||
impl ItemTier {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
ItemTier::NoTier => "NotAWeapon",
|
||||
ItemTier::T0 => "T0",
|
||||
ItemTier::T1 => "T1",
|
||||
ItemTier::T2 => "T2",
|
||||
ItemTier::T3 => "T3",
|
||||
ItemTier::T4 => "T4",
|
||||
ItemTier::T5 => "T5",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ItemType {
|
||||
NoFunction,
|
||||
Weapon,
|
||||
Module,
|
||||
Movement,
|
||||
Cosmetic,
|
||||
}
|
||||
|
||||
impl ItemType {
|
||||
fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::NoFunction => "NotAFunctionalItem",
|
||||
Self::Weapon => "Weapon",
|
||||
Self::Module => "Module",
|
||||
Self::Movement => "Movement",
|
||||
Self::Cosmetic => "Cosmetic",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn item_key(category: super::weapon_list::ItemCategory, tier: ItemTier) -> i32 {
|
||||
category.but_bigger() + (tier as i32)
|
||||
}
|
||||
38
rc_core/src/data/error_codes.rs
Normal file
38
rc_core/src/data/error_codes.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
#[repr(i16)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub enum WebServicesError {
|
||||
None = 0,
|
||||
CPUTooHigh = 1,
|
||||
CPUTooLow = 2,
|
||||
RobotTierNotAllowed = 3,
|
||||
CubeIDNotAllowed = 4,
|
||||
CubeTypeNotAllowed = 5,
|
||||
DatabaseError = 8,
|
||||
UnexpectedError = 9,
|
||||
WrongNumberOfAuthParams = 10,
|
||||
Banned = 11,
|
||||
EACValidationFailed = 12,
|
||||
NotSteamUser = 13,
|
||||
PromotionDoesntExist = 14,
|
||||
NotEnoughMoney = 17,
|
||||
MaxGarageSlots = 18,
|
||||
PlatformFeatureNotAvailable = 19,
|
||||
UserDoesNotHaveAllCubeTypes = 20,
|
||||
ReplaceDailyQuestLimit = 21,
|
||||
MaintenanceModeError = 125,
|
||||
RobotShopMaintenanceMode = 126,
|
||||
InvalidRobot = 140,
|
||||
ExpiredRobot = 144,
|
||||
RobotHasSanction = 145,
|
||||
CustomisationNotOwned = 146,
|
||||
ItemShopBundleExpired = 147,
|
||||
UserNotFound = 200,
|
||||
InvalidUsernameFormat = 201,
|
||||
UsernameTooLong = 202,
|
||||
InvalidUsername = 203,
|
||||
TencentValidationFail = 204,
|
||||
UsernameAlreadyTaken = 205,
|
||||
UsernameTooShort = 206,
|
||||
SaleEnded = 207
|
||||
}
|
||||
41
rc_core/src/data/game_mode.rs
Normal file
41
rc_core/src/data/game_mode.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use polariton::{operation::Typed, serdes::TypePrefix};
|
||||
|
||||
pub struct GameModeConfig {
|
||||
pub respawn_heal_duration: f32,
|
||||
pub respawn_full_heal_duration: f32,
|
||||
pub kill_limit: i32,
|
||||
pub game_time_minutes: i32,
|
||||
}
|
||||
|
||||
impl GameModeConfig {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::HashMap(vec![
|
||||
(Typed::Str("respawnHealDuration".into()), Typed::Float(self.respawn_heal_duration)),
|
||||
(Typed::Str("respawnFullHealDuration".into()), Typed::Float(self.respawn_full_heal_duration)),
|
||||
(Typed::Str("killLimit".into()), Typed::Int(self.kill_limit)),
|
||||
(Typed::Str("gameTimeMinutes".into()), Typed::Int(self.game_time_minutes)),
|
||||
].into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GameModeConfigs {
|
||||
pub battle_arena: GameModeConfig,
|
||||
pub elimination: GameModeConfig,
|
||||
pub the_pit: GameModeConfig,
|
||||
pub team_deathmatch: GameModeConfig,
|
||||
}
|
||||
|
||||
impl GameModeConfigs {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::Dict(polariton::operation::Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: vec![
|
||||
(Typed::Str("BattleArena".into()), self.battle_arena.as_transmissible()),
|
||||
(Typed::Str("Elimination".into()), self.elimination.as_transmissible()),
|
||||
(Typed::Str("ThePit".into()), self.the_pit.as_transmissible()),
|
||||
(Typed::Str("TeamDeathmatch".into()), self.team_deathmatch.as_transmissible()),
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
86
rc_core/src/data/garage_bay.rs
Normal file
86
rc_core/src/data/garage_bay.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use polariton::{operation::{Arr, Typed}, serdes::TypePrefix};
|
||||
|
||||
use super::weapon_list::ItemCategory;
|
||||
|
||||
pub struct GarageSlotInfo {
|
||||
pub name: String,
|
||||
pub cubes: u32,
|
||||
pub crf_id: u32, // 0 means not uploaded
|
||||
pub was_rated: bool, // ignored when not on CRF
|
||||
pub movement_categories: Vec<ItemCategory>,
|
||||
pub uuid: (u32, u32),
|
||||
pub thumbnail_version: u32,
|
||||
pub total_robot_cpu: u32,
|
||||
pub total_cosmetic_cpu: u32,
|
||||
pub total_robot_ranking: u32,
|
||||
pub bay_cpu: u32,
|
||||
pub tutorial_robot: bool, // assumed to be false (when omitted)
|
||||
pub starter_robot_index: i32, // assumed to be -1 (whem omitted)
|
||||
pub control_type: ControlType,
|
||||
pub control_options: ControlOptions,
|
||||
pub mastery_level: i32,
|
||||
pub bay_skin_id: String,
|
||||
pub weapon_order: Vec<i32>,
|
||||
}
|
||||
|
||||
impl GarageSlotInfo {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::HashMap(vec![
|
||||
(Typed::Str("name".into()), Typed::Str(self.name.clone().into())),
|
||||
(Typed::Str("numberCubes".into()), Typed::Int(self.cubes as i32)),
|
||||
(Typed::Str("crfId".into()), Typed::Int(self.crf_id as i32)),
|
||||
(Typed::Str("wasRated".into()), Typed::Bool(self.was_rated.into())),
|
||||
(Typed::Str("movementCategories".into()), Typed::Arr(Arr {
|
||||
ty: TypePrefix::Int, // int
|
||||
items: self.movement_categories.iter().map(|x| Typed::Int(x.but_bigger() as i32)).collect(),
|
||||
})),
|
||||
(Typed::Str("uniqueId1".into()), Typed::Int(self.uuid.0 as i32)),
|
||||
(Typed::Str("uniqueId2".into()), Typed::Int(self.uuid.1 as i32)),
|
||||
(Typed::Str("thumbnailVersion".into()), Typed::Int(self.thumbnail_version as i32)),
|
||||
(Typed::Str("totalRobotCPU".into()), Typed::Int(self.total_robot_cpu as i32)),
|
||||
(Typed::Str("totalCosmeticCPU".into()), Typed::Int(self.total_cosmetic_cpu as i32)),
|
||||
(Typed::Str("totalRobotRanking".into()), Typed::Int(self.total_robot_ranking as i32)),
|
||||
(Typed::Str("bayCpu".into()), Typed::Int(self.bay_cpu as i32)),
|
||||
(Typed::Str("tutorialRobot".into()), Typed::Bool(self.tutorial_robot.into())),
|
||||
(Typed::Str("starterRobotIndex".into()), Typed::Int(self.starter_robot_index)),
|
||||
(Typed::Str("controlType".into()), Typed::Int(self.control_type as i32)),
|
||||
(Typed::Str("controlOptions".into()), self.control_options.as_transmissible()),
|
||||
(Typed::Str("masteryLevel".into()), Typed::Int(self.mastery_level)),
|
||||
(Typed::Str("baySkinId".into()), Typed::Str(self.bay_skin_id.clone().into())),
|
||||
(Typed::Str("weaponOrder".into()), Typed::Arr(Arr {
|
||||
ty: TypePrefix::Int, // int
|
||||
items: self.weapon_order.iter().map(|x| Typed::Int(*x)).collect(),
|
||||
})),
|
||||
].into())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(i32)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ControlType {
|
||||
Camera = 0,
|
||||
Keyboard = 1,
|
||||
Count = 2,
|
||||
}
|
||||
|
||||
pub struct ControlOptions {
|
||||
pub vertical_strafing: bool,
|
||||
pub sideways_driving: bool,
|
||||
pub tracks_turn_on_spot: bool,
|
||||
}
|
||||
|
||||
impl ControlOptions {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::Arr(Arr {
|
||||
ty: TypePrefix::Bool, // bool
|
||||
items: vec![
|
||||
Typed::Bool(self.vertical_strafing.into()),
|
||||
Typed::Bool(self.sideways_driving.into()),
|
||||
Typed::Bool(self.tracks_turn_on_spot.into()),
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
39
rc_core/src/data/mod.rs
Normal file
39
rc_core/src/data/mod.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
pub mod auto_regen;
|
||||
pub mod campaign;
|
||||
pub mod cube_list;
|
||||
pub mod game_mode;
|
||||
pub mod garage_bay;
|
||||
pub mod movement_list;
|
||||
pub mod player_data;
|
||||
pub mod tech_tree;
|
||||
pub mod voting;
|
||||
pub mod weapon_list;
|
||||
pub mod weapon_upgrade;
|
||||
|
||||
pub mod error_codes;
|
||||
|
||||
pub fn encode_7_bit_i32(mut src: i32) -> Vec<u8> {
|
||||
if src == 0 { return vec![0] }
|
||||
let mut out = Vec::with_capacity(5);
|
||||
while src != 0 {
|
||||
let last_7 = (src & 0x7F) as u8;
|
||||
src = src >> 7;
|
||||
if src != 0 {
|
||||
out.push(last_7 | 0x80);
|
||||
} else {
|
||||
out.push(last_7);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn write_str_for_binreader(s: &str, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||
let s_bytes = s.as_bytes();
|
||||
let mut total_len = writer.write(&encode_7_bit_i32(s_bytes.len() as i32))?;
|
||||
total_len += writer.write(s_bytes)?;
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
pub fn cube_id_to_str(id: u32) -> String {
|
||||
hex::encode(id.to_be_bytes()).into()
|
||||
}
|
||||
455
rc_core/src/data/movement_list.rs
Normal file
455
rc_core/src/data/movement_list.rs
Normal file
@@ -0,0 +1,455 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use polariton::operation::{Typed, Dict};
|
||||
use polariton::serdes::TypePrefix;
|
||||
|
||||
use super::cube_list::ItemTier;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MovementCategoryData {
|
||||
pub horizontal_top_speed: Option<f32>,
|
||||
pub vertical_top_speed: Option<f32>,
|
||||
pub min_required_items: Option<i32>,
|
||||
pub min_item_modifier: Option<f32>,
|
||||
pub max_hover_height: Option<f32>,
|
||||
pub light_machine_mass: Option<f32>,
|
||||
pub heavy_machine_mass: Option<f32>,
|
||||
pub specifics: MovementCategorySpecificData,
|
||||
pub stats: Vec<(ItemTier, MovementData)>,
|
||||
}
|
||||
|
||||
impl MovementCategoryData {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
let mut out = Vec::new();
|
||||
self.horizontal_top_speed.map(|x| out.push((Typed::Str("horizontalTopSpeed".into()), Typed::Float(x))));
|
||||
self.vertical_top_speed.map(|x| out.push((Typed::Str("verticalTopSpeed".into()), Typed::Float(x))));
|
||||
self.min_required_items.map(|x| out.push((Typed::Str("minRequiredItems".into()), Typed::Int(x))));
|
||||
self.min_item_modifier.map(|x| out.push((Typed::Str("minItemsModifier".into()), Typed::Float(x))));
|
||||
self.max_hover_height.map(|x| out.push((Typed::Str("maxHoverHeight".into()), Typed::Float(x))));
|
||||
self.light_machine_mass.map(|x| out.push((Typed::Str("lightMachineMass".into()), Typed::Float(x))));
|
||||
self.heavy_machine_mass.map(|x| out.push((Typed::Str("heavyMachineMass".into()), Typed::Float(x))));
|
||||
out.append(&mut self.specifics.as_transmissible());
|
||||
for (tier, mov_data) in self.stats.iter() {
|
||||
out.push((Typed::Str(tier.as_str().into()), mov_data.as_transmissible()));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: out.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum MovementCategorySpecificData {
|
||||
#[default]
|
||||
Wheel,
|
||||
Hover(HoverCategoryData),
|
||||
Wing,
|
||||
Rudder, // same as wing
|
||||
Thruster,
|
||||
Propeller, // same as thruster
|
||||
InsectLeg,
|
||||
MechLeg(MechLegCategoryData),
|
||||
SprinterLeg(MechLegCategoryData), // same as mech leg
|
||||
TankTrack,
|
||||
Rotor(RotorCategoryData),
|
||||
Ski,
|
||||
}
|
||||
|
||||
impl MovementCategorySpecificData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
match self {
|
||||
Self::Wheel => Vec::default(),
|
||||
Self::Hover(x) => x.as_transmissible(),
|
||||
Self::Wing => Vec::default(),
|
||||
Self::Rudder => Vec::default(),
|
||||
Self::Thruster => Vec::default(),
|
||||
Self::Propeller => Vec::default(),
|
||||
Self::InsectLeg => Vec::default(),
|
||||
Self::MechLeg(x) => x.as_transmissible(),
|
||||
Self::SprinterLeg(x) => x.as_transmissible(),
|
||||
Self::TankTrack => Vec::default(),
|
||||
Self::Rotor(x) => x.as_transmissible(),
|
||||
Self::Ski => Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HoverCategoryData {
|
||||
pub height_tolerance: f32,
|
||||
pub force_y_offset: f32,
|
||||
pub turning_scale: f32,
|
||||
pub small_angle_turning_scale: f32,
|
||||
pub max_vertical_velocity: f32,
|
||||
pub hover_damping: f32,
|
||||
pub angular_damping: f32,
|
||||
pub deceleration_multiplier: f32,
|
||||
}
|
||||
|
||||
impl HoverCategoryData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("heightTolerance".into()), Typed::Float(self.height_tolerance)),
|
||||
(Typed::Str("forceYOffset".into()), Typed::Float(self.force_y_offset)),
|
||||
(Typed::Str("turningScale".into()), Typed::Float(self.turning_scale)),
|
||||
(Typed::Str("smallAngleTurningScale".into()), Typed::Float(self.small_angle_turning_scale)),
|
||||
(Typed::Str("verticalTopSpeed".into()), Typed::Float(self.max_vertical_velocity)),
|
||||
(Typed::Str("hoverDamping".into()), Typed::Float(self.hover_damping)),
|
||||
(Typed::Str("angularDamping".into()), Typed::Float(self.angular_damping)),
|
||||
(Typed::Str("decelerationMultiplier".into()), Typed::Float(self.deceleration_multiplier)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MechLegCategoryData {
|
||||
pub deceleration_multiplier: f32,
|
||||
}
|
||||
|
||||
impl MechLegCategoryData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("decelerationMultiplier".into()), Typed::Float(self.deceleration_multiplier)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RotorCategoryData {
|
||||
pub max_turn_rate: f32,
|
||||
}
|
||||
|
||||
|
||||
impl RotorCategoryData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("maxTurnRate".into()), Typed::Float(self.max_turn_rate)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MovementData {
|
||||
pub speed_boost: Option<f32>,
|
||||
pub max_carry_mass: Option<f32>,
|
||||
pub horizontal_top_speed: Option<f32>,
|
||||
pub vertical_top_speed: Option<f32>,
|
||||
pub specifics: MovementSpecificData,
|
||||
}
|
||||
|
||||
impl MovementData {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
let mut out = Vec::new();
|
||||
self.speed_boost.map(|x| out.push((Typed::Str("speedBoost".into()), Typed::Float(x))));
|
||||
self.max_carry_mass.map(|x| out.push((Typed::Str("maxCarryMass".into()), Typed::Float(x))));
|
||||
self.horizontal_top_speed.map(|x| out.push((Typed::Str("horizontalTopSpeed".into()), Typed::Float(x))));
|
||||
self.vertical_top_speed.map(|x| out.push((Typed::Str("verticalTopSpeed".into()), Typed::Float(x))));
|
||||
out.append(&mut self.specifics.as_transmissible());
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str, // str
|
||||
val_ty: TypePrefix::Any, // any
|
||||
items: out.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MovementSpecificData {
|
||||
Wheel(WheelData),
|
||||
Hover(HoverData),
|
||||
Wing(AerofoilData),
|
||||
Rudder(AerofoilData), // same as wing
|
||||
Thruster(ThrusterData),
|
||||
Propeller(ThrusterData), // same as thruster
|
||||
InsectLeg(InsectLegData),
|
||||
MechLeg(MechLegData),
|
||||
SprinterLeg(MechLegData), // same as mech leg
|
||||
TankTrack(TankTrackData),
|
||||
Rotor(RotorData),
|
||||
Ski,
|
||||
}
|
||||
|
||||
impl MovementSpecificData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
match self {
|
||||
Self::Wheel(x) => x.as_transmissible(),
|
||||
Self::Hover(x) => x.as_transmissible(),
|
||||
Self::Wing(x) => x.as_transmissible(),
|
||||
Self::Rudder(x) => x.as_transmissible(),
|
||||
Self::Thruster(x) => x.as_transmissible(),
|
||||
Self::Propeller(x) => x.as_transmissible(),
|
||||
Self::InsectLeg(x) => x.as_transmissible(),
|
||||
Self::MechLeg(x) => x.as_transmissible(),
|
||||
Self::SprinterLeg(x) => x.as_transmissible(),
|
||||
Self::TankTrack(x) => x.as_transmissible(),
|
||||
Self::Rotor(x) => x.as_transmissible(),
|
||||
Self::Ski => Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WheelData {
|
||||
pub steering_speed_light: f32,
|
||||
pub steering_speed_heavy: f32,
|
||||
pub steering_force_multiplier_light: f32,
|
||||
pub steering_force_multiplier_heavy: f32,
|
||||
pub lateral_acceleration_light: f32,
|
||||
pub lateral_acceleration_heavy: f32,
|
||||
pub time_to_max_acceleration_light: f32,
|
||||
pub time_to_max_acceleration_heavy: f32,
|
||||
pub brake_force_light: f32,
|
||||
pub brake_force_heavy: f32,
|
||||
}
|
||||
|
||||
impl WheelData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("steeringSpeedLight".into()), Typed::Float(self.steering_speed_light)),
|
||||
(Typed::Str("steeringSpeedHeavy".into()), Typed::Float(self.steering_speed_heavy)),
|
||||
(Typed::Str("steeringForceMultiplierLight".into()), Typed::Float(self.steering_force_multiplier_light)),
|
||||
(Typed::Str("steeringForceMultiplierHeavy".into()), Typed::Float(self.steering_force_multiplier_heavy)),
|
||||
(Typed::Str("lateralAccelerationLight".into()), Typed::Float(self.lateral_acceleration_light)),
|
||||
(Typed::Str("lateralAccelerationHeavy".into()), Typed::Float(self.lateral_acceleration_heavy)),
|
||||
(Typed::Str("timeToMaxAccelerationLight".into()), Typed::Float(self.time_to_max_acceleration_light)),
|
||||
(Typed::Str("timeToMaxAccelerationHeavy".into()), Typed::Float(self.time_to_max_acceleration_heavy)),
|
||||
(Typed::Str("brakeForceLight".into()), Typed::Float(self.brake_force_light)),
|
||||
(Typed::Str("brakeForceHeavy".into()), Typed::Float(self.brake_force_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HoverData {
|
||||
pub max_hover_height_light: f32,
|
||||
pub max_hover_height_heavy: f32,
|
||||
pub height_change_speed_light: f32,
|
||||
pub height_change_speed_heavy: f32,
|
||||
pub turn_torque_light: f32,
|
||||
pub turn_torque_heavy: f32,
|
||||
pub acceleration_light: f32,
|
||||
pub acceleration_heavy: f32,
|
||||
pub max_angular_velocity_light: f32,
|
||||
pub max_angular_velocity_heavy: f32,
|
||||
pub lateral_damping_light: f32,
|
||||
pub lateral_damping_heavy: f32,
|
||||
}
|
||||
|
||||
impl HoverData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("maxHoverHeightLight".into()), Typed::Float(self.max_hover_height_light)),
|
||||
(Typed::Str("maxHoverHeightHeavy".into()), Typed::Float(self.max_hover_height_heavy)),
|
||||
(Typed::Str("heightChangeSpeedLight".into()), Typed::Float(self.height_change_speed_light)),
|
||||
(Typed::Str("heightChangeSpeedHeavy".into()), Typed::Float(self.height_change_speed_heavy)),
|
||||
(Typed::Str("turnTorqueLight".into()), Typed::Float(self.turn_torque_light)),
|
||||
(Typed::Str("turnTorqueHeavy".into()), Typed::Float(self.turn_torque_heavy)),
|
||||
(Typed::Str("accelerationLight".into()), Typed::Float(self.acceleration_light)),
|
||||
(Typed::Str("accelerationHeavy".into()), Typed::Float(self.acceleration_heavy)),
|
||||
(Typed::Str("maxAngularVelocityLight".into()), Typed::Float(self.max_angular_velocity_light)),
|
||||
(Typed::Str("maxAngularVelocityHeavy".into()), Typed::Float(self.max_angular_velocity_heavy)),
|
||||
(Typed::Str("lateralDampingLight".into()), Typed::Float(self.lateral_damping_light)),
|
||||
(Typed::Str("lateralDampingHeavy".into()), Typed::Float(self.lateral_damping_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AerofoilData {
|
||||
pub barrel_speed_light: f32,
|
||||
pub barrel_speed_heavy: f32,
|
||||
pub bank_speed_light: f32,
|
||||
pub bank_speed_heavy: f32,
|
||||
pub elevation_speed_light: f32,
|
||||
pub elevation_speed_heavy: f32,
|
||||
pub rudder_speed_light: f32,
|
||||
pub rudder_speed_heavy: f32,
|
||||
pub thrust_light: f32,
|
||||
pub thrust_heavy: f32,
|
||||
pub vtol_velocity_light: f32,
|
||||
pub vtol_velocity_heavy: f32,
|
||||
}
|
||||
|
||||
impl AerofoilData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("barrelSpeedLight".into()), Typed::Float(self.barrel_speed_light)),
|
||||
(Typed::Str("barrelSpeedHeavy".into()), Typed::Float(self.barrel_speed_heavy)),
|
||||
(Typed::Str("bankSpeedLight".into()), Typed::Float(self.bank_speed_light)),
|
||||
(Typed::Str("bankSpeedHeavy".into()), Typed::Float(self.bank_speed_heavy)),
|
||||
(Typed::Str("elevationSpeedLight".into()), Typed::Float(self.elevation_speed_light)),
|
||||
(Typed::Str("elevationSpeedHeavy".into()), Typed::Float(self.elevation_speed_heavy)),
|
||||
(Typed::Str("rudderSpeedLight".into()), Typed::Float(self.rudder_speed_light)),
|
||||
(Typed::Str("rudderSpeedHeavy".into()), Typed::Float(self.rudder_speed_heavy)),
|
||||
(Typed::Str("thrustLight".into()), Typed::Float(self.thrust_light)),
|
||||
(Typed::Str("thrustHeavy".into()), Typed::Float(self.thrust_heavy)),
|
||||
(Typed::Str("vtolVelocityLight".into()), Typed::Float(self.vtol_velocity_light)),
|
||||
(Typed::Str("vtolVelocityHeavy".into()), Typed::Float(self.vtol_velocity_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThrusterData {
|
||||
pub acceleration_delay_light: f32,
|
||||
pub acceleration_delay_heavy: f32,
|
||||
}
|
||||
|
||||
impl ThrusterData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("accelerationDelayLight".into()), Typed::Float(self.acceleration_delay_light)),
|
||||
(Typed::Str("accelerationDelayHeavy".into()), Typed::Float(self.acceleration_delay_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InsectLegData {
|
||||
pub ideal_height_light: f32,
|
||||
pub ideal_height_heavy: f32,
|
||||
pub ideal_crouching_height_light: f32,
|
||||
pub ideal_crouching_height_heavy: f32,
|
||||
pub ideal_height_range_light: f32,
|
||||
pub ideal_height_range_heavy: f32,
|
||||
pub jump_height_light: f32,
|
||||
pub jump_height_heavy: f32,
|
||||
pub max_upwards_force_light: f32,
|
||||
pub max_upwards_force_heavy: f32,
|
||||
pub max_lateral_force_light: f32,
|
||||
pub max_lateral_force_heavy: f32,
|
||||
pub max_turning_force_light: f32,
|
||||
pub max_turning_force_heavy: f32,
|
||||
pub max_damping_force_light: f32,
|
||||
pub max_damping_force_heavy: f32,
|
||||
pub max_stopped_force_light: f32,
|
||||
pub max_stopped_force_heavy: f32,
|
||||
pub max_new_stopped_force_light: f32,
|
||||
pub max_new_stopped_force_heavy: f32,
|
||||
pub upwards_damping_force_light: f32,
|
||||
pub upwards_damping_force_heavy: f32,
|
||||
pub lateral_damp_force_light: f32,
|
||||
pub lateral_damp_force_heavy: f32,
|
||||
pub swagger_force_light: f32,
|
||||
pub swagger_force_heavy: f32,
|
||||
}
|
||||
|
||||
impl InsectLegData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("idealHeightLight".into()), Typed::Float(self.ideal_height_light)),
|
||||
(Typed::Str("idealHeightHeavy".into()), Typed::Float(self.ideal_height_heavy)),
|
||||
(Typed::Str("idealCrouchingHeightLight".into()), Typed::Float(self.ideal_crouching_height_light)),
|
||||
(Typed::Str("idealCrouchingHeightHeavy".into()), Typed::Float(self.ideal_crouching_height_heavy)),
|
||||
(Typed::Str("idealHeightRangeLight".into()), Typed::Float(self.ideal_height_range_light)),
|
||||
(Typed::Str("idealHeightRangeHeavy".into()), Typed::Float(self.ideal_height_range_heavy)),
|
||||
(Typed::Str("jumpHeightLight".into()), Typed::Float(self.jump_height_light)),
|
||||
(Typed::Str("jumpHeightHeavy".into()), Typed::Float(self.jump_height_heavy)),
|
||||
(Typed::Str("maxUpwardsForceLight".into()), Typed::Float(self.max_upwards_force_light)),
|
||||
(Typed::Str("maxUpwardsForceHeavy".into()), Typed::Float(self.max_upwards_force_heavy)),
|
||||
(Typed::Str("maxLateralForceLight".into()), Typed::Float(self.max_lateral_force_light)),
|
||||
(Typed::Str("maxLateralForceHeavy".into()), Typed::Float(self.max_lateral_force_heavy)),
|
||||
(Typed::Str("maxTurningForceLight".into()), Typed::Float(self.max_turning_force_light)),
|
||||
(Typed::Str("maxTurningForceHeavy".into()), Typed::Float(self.max_turning_force_heavy)),
|
||||
(Typed::Str("maxDampingForceLight".into()), Typed::Float(self.max_damping_force_light)),
|
||||
(Typed::Str("maxDampingForceHeavy".into()), Typed::Float(self.max_damping_force_heavy)),
|
||||
(Typed::Str("maxStoppedForceLight".into()), Typed::Float(self.max_stopped_force_light)),
|
||||
(Typed::Str("maxStoppedForceHeavy".into()), Typed::Float(self.max_stopped_force_heavy)),
|
||||
(Typed::Str("maxNewStoppedForceLight".into()), Typed::Float(self.max_new_stopped_force_light)),
|
||||
(Typed::Str("maxNewStoppedForceHeavy".into()), Typed::Float(self.max_new_stopped_force_heavy)),
|
||||
(Typed::Str("upwardsDampingForceLight".into()), Typed::Float(self.upwards_damping_force_light)),
|
||||
(Typed::Str("upwardsDampingForceHeavy".into()), Typed::Float(self.upwards_damping_force_heavy)),
|
||||
(Typed::Str("lateralDampForceLight".into()), Typed::Float(self.lateral_damp_force_light)),
|
||||
(Typed::Str("lateralDampForceHeavy".into()), Typed::Float(self.lateral_damp_force_heavy)),
|
||||
(Typed::Str("swaggerForceLight".into()), Typed::Float(self.swagger_force_light)),
|
||||
(Typed::Str("swaggerForceHeavy".into()), Typed::Float(self.swagger_force_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MechLegData {
|
||||
pub time_grounded_after_jump_light: f32,
|
||||
pub time_grounded_after_jump_heavy: f32,
|
||||
pub jump_height_light: f32,
|
||||
pub jump_height_heavy: f32,
|
||||
pub turn_acceleration_light: f32,
|
||||
pub turn_acceleration_heavy: f32,
|
||||
pub legacy_turn_acceleration_light: f32,
|
||||
pub legacy_turn_acceleration_heavy: f32,
|
||||
pub long_jump_speed_scale_light: f32,
|
||||
pub long_jump_speed_scale_heavy: f32,
|
||||
pub max_lateral_force_light: f32,
|
||||
pub max_lateral_force_heavy: f32,
|
||||
pub max_damping_force_light: f32,
|
||||
pub max_damping_force_heavy: f32,
|
||||
}
|
||||
|
||||
impl MechLegData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("timeGroundedAfterJumpLight".into()), Typed::Float(self.time_grounded_after_jump_light)),
|
||||
(Typed::Str("timeGroundedAfterJumpHeavy".into()), Typed::Float(self.time_grounded_after_jump_heavy)),
|
||||
(Typed::Str("jumpHeightLight".into()), Typed::Float(self.jump_height_light)),
|
||||
(Typed::Str("jumpHeightHeavy".into()), Typed::Float(self.jump_height_heavy)),
|
||||
(Typed::Str("turnAccelerationLight".into()), Typed::Float(self.turn_acceleration_light)),
|
||||
(Typed::Str("turnAccelerationHeavy".into()), Typed::Float(self.turn_acceleration_heavy)),
|
||||
(Typed::Str("legacyTurnAccelerationLight".into()), Typed::Float(self.legacy_turn_acceleration_light)),
|
||||
(Typed::Str("legacyTurnAccelerationHeavy".into()), Typed::Float(self.legacy_turn_acceleration_heavy)),
|
||||
(Typed::Str("longJumpSpeedScaleLight".into()), Typed::Float(self.long_jump_speed_scale_light)),
|
||||
(Typed::Str("longJumpSpeedScaleHeavy".into()), Typed::Float(self.long_jump_speed_scale_heavy)),
|
||||
(Typed::Str("maxLateralForceLight".into()), Typed::Float(self.max_lateral_force_light)),
|
||||
(Typed::Str("maxLateralForceHeavy".into()), Typed::Float(self.max_lateral_force_heavy)),
|
||||
(Typed::Str("maxDampingForceLight".into()), Typed::Float(self.max_damping_force_light)),
|
||||
(Typed::Str("maxDampingForceHeavy".into()), Typed::Float(self.max_damping_force_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TankTrackData {
|
||||
pub max_turn_rate_moving_light: f32,
|
||||
pub max_turn_rate_moving_heavy: f32,
|
||||
pub max_turn_rate_stopped_light: f32,
|
||||
pub max_turn_rate_stopped_heavy: f32,
|
||||
pub turn_acceleration_light: f32,
|
||||
pub turn_acceleration_heavy: f32,
|
||||
pub lateral_acceleration_light: f32,
|
||||
pub lateral_acceleration_heavy: f32,
|
||||
}
|
||||
|
||||
impl TankTrackData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("maxTurnRateMovingLight".into()), Typed::Float(self.max_turn_rate_moving_light)),
|
||||
(Typed::Str("maxTurnRateMovingHeavy".into()), Typed::Float(self.max_turn_rate_moving_heavy)),
|
||||
(Typed::Str("maxTurnRateStoppedLight".into()), Typed::Float(self.max_turn_rate_stopped_light)),
|
||||
(Typed::Str("maxTurnRateStoppedHeavy".into()), Typed::Float(self.max_turn_rate_stopped_heavy)),
|
||||
(Typed::Str("turnAccelerationLight".into()), Typed::Float(self.turn_acceleration_light)),
|
||||
(Typed::Str("turnAccelerationHeavy".into()), Typed::Float(self.turn_acceleration_heavy)),
|
||||
(Typed::Str("lateralAccelerationLight".into()), Typed::Float(self.lateral_acceleration_light)),
|
||||
(Typed::Str("lateralAccelerationHeavy".into()), Typed::Float(self.lateral_acceleration_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RotorData {
|
||||
pub height_acceleration_light: f32,
|
||||
pub height_acceleration_heavy: f32,
|
||||
pub strafe_acceleration_light: f32,
|
||||
pub strafe_acceleration_heavy: f32,
|
||||
pub turn_acceleration_light: f32,
|
||||
pub turn_acceleration_heavy: f32,
|
||||
pub height_max_change_speed_light: f32,
|
||||
pub height_max_change_speed_heavy: f32,
|
||||
pub level_acceleration_light: f32,
|
||||
pub level_acceleration_heavy: f32,
|
||||
}
|
||||
|
||||
|
||||
impl RotorData {
|
||||
pub fn as_transmissible<C>(&self) -> Vec<(Typed<C>, Typed<C>)> {
|
||||
vec![
|
||||
(Typed::Str("heightAccelerationLight".into()), Typed::Float(self.height_acceleration_light)),
|
||||
(Typed::Str("heightAccelerationHeavy".into()), Typed::Float(self.height_acceleration_heavy)),
|
||||
(Typed::Str("strafeAccelerationLight".into()), Typed::Float(self.strafe_acceleration_light)),
|
||||
(Typed::Str("strafeAccelerationHeavy".into()), Typed::Float(self.strafe_acceleration_heavy)),
|
||||
(Typed::Str("turnAccelerationLight".into()), Typed::Float(self.turn_acceleration_light)),
|
||||
(Typed::Str("turnAccelerationHeavy".into()), Typed::Float(self.turn_acceleration_heavy)),
|
||||
(Typed::Str("heightMaxChangeSpeedLight".into()), Typed::Float(self.height_max_change_speed_light)),
|
||||
(Typed::Str("heightMaxChangeSpeedHeavy".into()), Typed::Float(self.height_max_change_speed_heavy)),
|
||||
(Typed::Str("levelAccelerationLight".into()), Typed::Float(self.level_acceleration_light)),
|
||||
(Typed::Str("levelAccelerationHeavy".into()), Typed::Float(self.level_acceleration_heavy)),
|
||||
]
|
||||
}
|
||||
}
|
||||
77
rc_core/src/data/player_data.rs
Normal file
77
rc_core/src/data/player_data.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use polariton::operation::Typed;
|
||||
|
||||
pub struct PlayerData {
|
||||
pub name: String,
|
||||
pub display_name: String,
|
||||
pub mastery: i32,
|
||||
pub tier: i32,
|
||||
pub robot_name: String,
|
||||
pub robot_map: Vec<u8>,
|
||||
// -- unused i32 here --
|
||||
pub team: i32,
|
||||
pub has_premium: bool,
|
||||
pub robot_uuid: String,
|
||||
pub cpu: i32,
|
||||
pub weapon_order: Vec<i32>,
|
||||
pub colour_map: Vec<u8>,
|
||||
pub is_ai: bool,
|
||||
pub spawn_effect: String,
|
||||
pub death_effect: String,
|
||||
pub player_rank: i32,
|
||||
pub weapon_rank: std::collections::HashMap<i32, i32>,
|
||||
}
|
||||
|
||||
impl PlayerData {
|
||||
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)?;
|
||||
total_len += super::write_str_for_binreader(&self.display_name, writer)?;
|
||||
writer.write_all(&self.mastery.to_le_bytes())?;
|
||||
writer.write_all(&self.tier.to_le_bytes())?;
|
||||
total_len += super::write_str_for_binreader(&self.robot_name, writer)?;
|
||||
writer.write_all(&(self.robot_map.len() as i32).to_le_bytes())?;
|
||||
writer.write_all(&self.robot_map)?;
|
||||
writer.write_all(&[0xDE, 0xAD, 0xBE, 0xEF])?;
|
||||
writer.write_all(&self.team.to_le_bytes())?;
|
||||
writer.write_all(&[self.has_premium as u8])?;
|
||||
total_len += super::write_str_for_binreader(&self.robot_uuid, writer)?;
|
||||
writer.write_all(&self.cpu.to_le_bytes())?;
|
||||
writer.write_all(&(self.weapon_order.len() as i32).to_le_bytes())?;
|
||||
for weapon_key in self.weapon_order.iter() {
|
||||
writer.write_all(&weapon_key.to_le_bytes())?;
|
||||
}
|
||||
writer.write_all(&(self.colour_map.len() as i32).to_le_bytes())?;
|
||||
writer.write_all(&self.colour_map)?;
|
||||
writer.write_all(&[self.is_ai as u8])?;
|
||||
total_len += super::write_str_for_binreader(&self.spawn_effect, writer)?;
|
||||
total_len += super::write_str_for_binreader(&self.death_effect, writer)?;
|
||||
writer.write_all(&self.player_rank.to_le_bytes())?;
|
||||
writer.write_all(&(self.weapon_rank.len() as i32).to_le_bytes())?;
|
||||
for (key, val) in self.weapon_rank.iter() {
|
||||
writer.write_all(&key.to_le_bytes())?;
|
||||
writer.write_all(&val.to_le_bytes())?;
|
||||
}
|
||||
Ok(42 + self.robot_map.len() + (self.weapon_order.len() * 4) + self.colour_map.len() + (self.weapon_rank.len() * 8) + total_len)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PlayerDatas {
|
||||
pub players: Vec<PlayerData>,
|
||||
}
|
||||
|
||||
impl PlayerDatas {
|
||||
fn dump(&self, writer: &mut dyn std::io::Write) -> std::io::Result<usize> {
|
||||
writer.write_all(&(self.players.len() as i32).to_le_bytes())?;
|
||||
let mut total_len = 4;
|
||||
for data in self.players.iter() {
|
||||
total_len += data.dump(writer)?;
|
||||
}
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
let mut buf = Vec::new();
|
||||
let write_size = self.dump(&mut std::io::Cursor::new(&mut buf)).unwrap();
|
||||
log::debug!("PlayerDatas serialized to {} bytes: {:?}", write_size, buf);
|
||||
Typed::Bytes(buf.into())
|
||||
}
|
||||
}
|
||||
32
rc_core/src/data/tech_tree.rs
Normal file
32
rc_core/src/data/tech_tree.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use polariton::{operation::{Arr, Typed}, serdes::TypePrefix};
|
||||
|
||||
pub struct TechTreeNode {
|
||||
pub main_cube_id: i32, // hex
|
||||
pub position_x: i32,
|
||||
pub position_y: i32,
|
||||
pub is_unlocked: bool,
|
||||
pub is_unlockable: bool,
|
||||
pub tech_points: u32,
|
||||
pub neighbours: Vec<i32>, // cube IDs, hex
|
||||
}
|
||||
|
||||
impl TechTreeNode {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::HashMap(vec![
|
||||
(Typed::Str("mainCubeId".into()), Typed::Str(hex::encode(self.main_cube_id.to_be_bytes()).into())),
|
||||
(Typed::Str("positionX".into()), Typed::Int(self.position_x)),
|
||||
(Typed::Str("positionY".into()), Typed::Int(self.position_y)),
|
||||
(Typed::Str("isUnlocked".into()), Typed::Bool(self.is_unlocked.into())),
|
||||
(Typed::Str("isUnlockable".into()), Typed::Bool(self.is_unlockable.into())),
|
||||
(Typed::Str("tp".into()), Typed::Int(self.tech_points as i32)),
|
||||
(Typed::Str("neighbours".into()), Typed::Arr(Arr {
|
||||
ty: TypePrefix::Str, // str
|
||||
items: self.neighbours.iter().map(|cube_id| Typed::Str(hex::encode(cube_id.to_be_bytes()).into())).collect(),
|
||||
})),
|
||||
].into())
|
||||
}
|
||||
|
||||
pub fn as_transmissible_key_val<C>(&self) -> (Typed<C>, Typed<C>) {
|
||||
(Typed::Str(hex::encode(self.main_cube_id.to_be_bytes()).into()), self.as_transmissible())
|
||||
}
|
||||
}
|
||||
37
rc_core/src/data/voting.rs
Normal file
37
rc_core/src/data/voting.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use polariton::{operation::Typed, serdes::TypePrefix};
|
||||
|
||||
pub struct VoteThresholdData {
|
||||
pub name: String,
|
||||
pub localised_name: String,
|
||||
pub color: String,
|
||||
pub votes_required: i32,
|
||||
}
|
||||
|
||||
impl VoteThresholdData {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::Dict(polariton::operation::Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vec![
|
||||
(Typed::Str("name".into()), Typed::Str(self.name.clone().into())),
|
||||
(Typed::Str("localisedName".into()), Typed::Str(self.localised_name.clone().into())),
|
||||
(Typed::Str("color".into()), Typed::Str(self.color.clone().into())),
|
||||
(Typed::Str("votesRequired".into()), Typed::Int(self.votes_required)),
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Vote {
|
||||
BestPlayed = 0,
|
||||
BestLooking = 1,
|
||||
}
|
||||
|
||||
impl Vote {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::BestPlayed => "BestPlayed",
|
||||
Self::BestLooking => "BestLooking",
|
||||
}
|
||||
}
|
||||
}
|
||||
220
rc_core/src/data/weapon_list.rs
Normal file
220
rc_core/src/data/weapon_list.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
#![allow(dead_code)]
|
||||
use polariton::operation::Typed;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WeaponData {
|
||||
pub damage_inflicted: Option<i32>,
|
||||
pub protonium_damage_scale: Option<f32>,
|
||||
pub projectile_speed: Option<f32>,
|
||||
pub projectile_range: Option<f32>,
|
||||
pub base_inaccuracy: Option<f32>,
|
||||
pub base_air_inaccuracy: Option<f32>,
|
||||
pub movement_inaccuracy: Option<f32>,
|
||||
pub movement_max_speed: Option<f32>,
|
||||
pub movement_min_speed: Option<f32>,
|
||||
pub gun_rotation_slow: Option<f32>,
|
||||
pub movement_inaccuracy_decay: Option<f32>,
|
||||
pub slow_rotation_decay: Option<f32>,
|
||||
pub quick_rotation_decay: Option<f32>,
|
||||
pub movement_inaccuracy_recovery: Option<f32>,
|
||||
pub repeat_fire_inaccuracy_total_degrees: Option<f32>,
|
||||
pub repeat_fire_inaccuracy_decay: Option<f32>,
|
||||
pub repeat_fire_innaccuracy_recovery: Option<f32>,
|
||||
pub fire_instant_accuracy_decay: Option<f32>, // degrees
|
||||
pub accuracy_non_recover_time: Option<f32>,
|
||||
pub accuracy_decay: Option<f32>,
|
||||
pub damage_radius: Option<f32>,
|
||||
pub plasma_time_to_full_damage: Option<f32>,
|
||||
pub plasma_starting_radius_scale: Option<f32>,
|
||||
pub nano_dps: Option<f32>,
|
||||
pub nano_hps: Option<f32>,
|
||||
pub tesla_damage: Option<f32>,
|
||||
pub tesla_charges: Option<f32>,
|
||||
pub aeroflak_proximity_damage: Option<f32>,
|
||||
pub aeroflak_damage_radius: Option<f32>,
|
||||
pub aeroflak_explosion_radius: Option<f32>,
|
||||
pub aeroflak_ground_clearance: Option<f32>,
|
||||
pub aeroflak_max_stacks: Option<i32>,
|
||||
pub aeroflak_damage_per_stack: Option<i32>,
|
||||
pub aeroflak_stack_expire: Option<f32>,
|
||||
pub shot_cooldown: Option<f32>,
|
||||
pub smart_rotation_cooldown: Option<f32>,
|
||||
pub smart_rotation_cooldown_extra: Option<f32>,
|
||||
pub smart_rotation_max_stacks: Option<f32>,
|
||||
pub spin_up_time: Option<f32>,
|
||||
pub spin_down_time: Option<f32>,
|
||||
pub spin_initial_cooldown: Option<f32>,
|
||||
pub group_fire_scales: Vec<f32>,
|
||||
pub mana_cost: Option<f32>,
|
||||
pub lock_time: Option<f32>,
|
||||
pub full_lock_release: Option<f32>,
|
||||
pub change_lock_time: Option<f32>,
|
||||
pub max_rotation_speed: Option<f32>,
|
||||
pub initial_rotation_speed: Option<f32>,
|
||||
pub rotation_acceleration: Option<f32>,
|
||||
pub nano_healing_priority_time: Option<f32>,
|
||||
pub module_range: Option<f32>,
|
||||
pub shield_lifetime: Option<f32>,
|
||||
pub teleport_time: Option<f32>,
|
||||
pub camera_time: Option<f32>,
|
||||
pub camera_delay: Option<f32>,
|
||||
pub to_invisible_speed: Option<f32>,
|
||||
pub to_invisible_duration: Option<f32>,
|
||||
pub to_visible_duration: Option<f32>,
|
||||
pub countdown_time: Option<f32>,
|
||||
pub stun_time: Option<f32>,
|
||||
pub stun_radius: Option<f32>,
|
||||
pub effect_duration: Option<f32>,
|
||||
}
|
||||
|
||||
impl WeaponData {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
let mut out = Vec::new();
|
||||
|
||||
self.damage_inflicted.map(|x| out.push((Typed::Str("damageInflicted".into()), Typed::Int(x))));
|
||||
self.protonium_damage_scale.map(|x| out.push((Typed::Str("protoniumDamageScale".into()), Typed::Float(x))));
|
||||
self.projectile_speed.map(|x| out.push((Typed::Str("projectileSpeed".into()), Typed::Float(x))));
|
||||
self.projectile_range.map(|x| out.push((Typed::Str("projectileRange".into()), Typed::Float(x))));
|
||||
self.base_inaccuracy.map(|x| out.push((Typed::Str("baseInaccuracy".into()), Typed::Float(x))));
|
||||
self.base_air_inaccuracy.map(|x| out.push((Typed::Str("baseAirInaccuracy".into()), Typed::Float(x))));
|
||||
self.movement_inaccuracy.map(|x| out.push((Typed::Str("movementInaccuracy".into()), Typed::Float(x))));
|
||||
self.movement_max_speed.map(|x| out.push((Typed::Str("movementMaxThresholdSpeed".into()), Typed::Float(x))));
|
||||
self.movement_min_speed.map(|x| out.push((Typed::Str("movementMinThresholdSpeed".into()), Typed::Float(x))));
|
||||
self.gun_rotation_slow.map(|x| out.push((Typed::Str("gunRotationThresholdSlow".into()), Typed::Float(x))));
|
||||
self.movement_inaccuracy_decay.map(|x| out.push((Typed::Str("movementInaccuracyDecayTime".into()), Typed::Float(x))));
|
||||
self.slow_rotation_decay.map(|x| out.push((Typed::Str("slowRotationInaccuracyDecayTime".into()), Typed::Float(x))));
|
||||
self.quick_rotation_decay.map(|x| out.push((Typed::Str("quickRotationInaccuracyDecayTime".into()), Typed::Float(x))));
|
||||
self.movement_inaccuracy_recovery.map(|x| out.push((Typed::Str("movementInaccuracyRecoveryTime".into()), Typed::Float(x))));
|
||||
self.repeat_fire_inaccuracy_total_degrees.map(|x| out.push((Typed::Str("repeatFireInaccuracyTotalDegrees".into()), Typed::Float(x))));
|
||||
self.repeat_fire_inaccuracy_decay.map(|x| out.push((Typed::Str("repeatFireInaccuracyDecayTime".into()), Typed::Float(x))));
|
||||
self.repeat_fire_innaccuracy_recovery.map(|x| out.push((Typed::Str("repeatFireInaccuracyRecoveryTime".into()), Typed::Float(x))));
|
||||
self.fire_instant_accuracy_decay.map(|x| out.push((Typed::Str("fireInstantAccuracyDecayDegrees".into()), Typed::Float(x)))); // degrees
|
||||
self.accuracy_non_recover_time.map(|x| out.push((Typed::Str("accuracyNonRecoverTime".into()), Typed::Float(x))));
|
||||
self.accuracy_decay.map(|x| out.push((Typed::Str("accuracyDecayTime".into()), Typed::Float(x))));
|
||||
self.damage_radius.map(|x| out.push((Typed::Str("damageRadius".into()), Typed::Float(x))));
|
||||
self.plasma_time_to_full_damage.map(|x| out.push((Typed::Str("plasmaTimeToFullDamage".into()), Typed::Float(x))));
|
||||
self.plasma_starting_radius_scale.map(|x| out.push((Typed::Str("plasmaStartingRadiusScale".into()), Typed::Float(x))));
|
||||
self.nano_dps.map(|x| out.push((Typed::Str("nanoDPS".into()), Typed::Float(x))));
|
||||
self.nano_hps.map(|x| out.push((Typed::Str("nanoHPS".into()), Typed::Float(x))));
|
||||
self.tesla_damage.map(|x| out.push((Typed::Str("teslaDamage".into()), Typed::Float(x))));
|
||||
self.tesla_charges.map(|x| out.push((Typed::Str("teslaCharges".into()), Typed::Float(x))));
|
||||
self.aeroflak_proximity_damage.map(|x| out.push((Typed::Str("aeroflakProximityDamage".into()), Typed::Float(x))));
|
||||
self.aeroflak_damage_radius.map(|x| out.push((Typed::Str("aeroflakDamageRadius".into()), Typed::Float(x))));
|
||||
self.aeroflak_explosion_radius.map(|x| out.push((Typed::Str("aeroflakExplosionRadius".into()), Typed::Float(x))));
|
||||
self.aeroflak_ground_clearance.map(|x| out.push((Typed::Str("aeroflakGroundClearance".into()), Typed::Float(x))));
|
||||
self.aeroflak_max_stacks.map(|x| out.push((Typed::Str("aeroflakBuffMaxStacks".into()), Typed::Int(x))));
|
||||
self.aeroflak_damage_per_stack.map(|x| out.push((Typed::Str("aeroflakBuffDamagePerStack".into()), Typed::Int(x))));
|
||||
self.aeroflak_stack_expire.map(|x| out.push((Typed::Str("aeroflakBuffTimeToExpire".into()), Typed::Float(x))));
|
||||
self.shot_cooldown.map(|x| out.push((Typed::Str("cooldownBetweenShots".into()), Typed::Float(x))));
|
||||
self.smart_rotation_cooldown.map(|x| out.push((Typed::Str("smartRotationCooldown".into()), Typed::Float(x))));
|
||||
self.smart_rotation_cooldown_extra.map(|x| out.push((Typed::Str("smartRotationExtraCooldownTime".into()), Typed::Float(x))));
|
||||
self.smart_rotation_max_stacks.map(|x| out.push((Typed::Str("smartRotationMaxStacks".into()), Typed::Float(x))));
|
||||
self.spin_up_time.map(|x| out.push((Typed::Str("spinUpTime".into()), Typed::Float(x))));
|
||||
self.spin_down_time.map(|x| out.push((Typed::Str("spinDownTime".into()), Typed::Float(x))));
|
||||
self.spin_initial_cooldown.map(|x| out.push((Typed::Str("spinInitialCooldown".into()), Typed::Float(x))));
|
||||
|
||||
if !self.group_fire_scales.is_empty() {
|
||||
let typed_arr: Vec<Typed<C>> = self.group_fire_scales.iter().map(|x| Typed::Float(*x)).collect();
|
||||
out.push((Typed::Str("groupFireScales".into()), Typed::Arr(polariton::operation::Arr {
|
||||
ty: polariton::serdes::TypePrefix::Float,
|
||||
items: typed_arr,
|
||||
})));
|
||||
}
|
||||
|
||||
self.mana_cost.map(|x| out.push((Typed::Str("manaCost".into()), Typed::Float(x))));
|
||||
self.lock_time.map(|x| out.push((Typed::Str("lockTime".into()), Typed::Float(x))));
|
||||
self.full_lock_release.map(|x| out.push((Typed::Str("fullLockRelease".into()), Typed::Float(x))));
|
||||
self.change_lock_time.map(|x| out.push((Typed::Str("changeLockTime".into()), Typed::Float(x))));
|
||||
self.max_rotation_speed.map(|x| out.push((Typed::Str("maxRotationSpeed".into()), Typed::Float(x))));
|
||||
self.initial_rotation_speed.map(|x| out.push((Typed::Str("initialRotationSpeed".into()), Typed::Float(x))));
|
||||
self.rotation_acceleration.map(|x| out.push((Typed::Str("rotationAcceleration".into()), Typed::Float(x))));
|
||||
self.nano_healing_priority_time.map(|x| out.push((Typed::Str("nanoHealingPriorityTime".into()), Typed::Float(x))));
|
||||
self.module_range.map(|x| out.push((Typed::Str("moduleRange".into()), Typed::Float(x))));
|
||||
self.shield_lifetime.map(|x| out.push((Typed::Str("shieldLifetime".into()), Typed::Float(x))));
|
||||
self.teleport_time.map(|x| out.push((Typed::Str("teleportTime".into()), Typed::Float(x))));
|
||||
self.camera_time.map(|x| out.push((Typed::Str("cameraTime".into()), Typed::Float(x))));
|
||||
self.camera_delay.map(|x| out.push((Typed::Str("cameraDelay".into()), Typed::Float(x))));
|
||||
self.to_invisible_speed.map(|x| out.push((Typed::Str("toInvisibleSpeed".into()), Typed::Float(x))));
|
||||
self.to_invisible_duration.map(|x| out.push((Typed::Str("toInvisibleDuration".into()), Typed::Float(x))));
|
||||
self.to_visible_duration.map(|x| out.push((Typed::Str("toVisibleDuration".into()), Typed::Float(x))));
|
||||
self.countdown_time.map(|x| out.push((Typed::Str("countdownTime".into()), Typed::Float(x))));
|
||||
self.stun_time.map(|x| out.push((Typed::Str("stunTime".into()), Typed::Float(x))));
|
||||
self.stun_radius.map(|x| out.push((Typed::Str("stunRadius".into()), Typed::Float(x))));
|
||||
self.effect_duration.map(|x| out.push((Typed::Str("effectDuration".into()), Typed::Float(x))));
|
||||
Typed::HashMap(out.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ItemCategory {
|
||||
NoFunction = 0,
|
||||
Wheel = 1,
|
||||
Hover = 2,
|
||||
Wing = 3,
|
||||
Rudder = 4,
|
||||
Thruster = 5,
|
||||
InsectLeg = 6,
|
||||
MechLeg = 7,
|
||||
Ski = 8,
|
||||
TankTrack = 9,
|
||||
Rotor = 10,
|
||||
SprinterLeg = 11,
|
||||
Propeller = 12,
|
||||
Laser = 100,
|
||||
Plasma = 200,
|
||||
Mortar = 250,
|
||||
Rail = 300,
|
||||
Nano = 400,
|
||||
Tesla = 500,
|
||||
Aeroflak = 600,
|
||||
Ion = 650,
|
||||
Seeker = 701,
|
||||
Chaingun = 750,
|
||||
ShieldModule = 800,
|
||||
GhostModule = 801,
|
||||
BlinkModule = 802,
|
||||
EmpModule = 803,
|
||||
WindowmakerModule = 804,
|
||||
EnergyModule = 900,
|
||||
}
|
||||
|
||||
impl ItemCategory {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
ItemCategory::NoFunction => "NotAFunctionalItem",
|
||||
ItemCategory::Wheel => "Wheel",
|
||||
ItemCategory::Hover => "Hover",
|
||||
ItemCategory::Wing => "Wing",
|
||||
ItemCategory::Rudder => "Rudder",
|
||||
ItemCategory::Thruster => "Thruster",
|
||||
ItemCategory::InsectLeg => "InsectLeg",
|
||||
ItemCategory::MechLeg => "MechLeg",
|
||||
ItemCategory::Ski => "Ski",
|
||||
ItemCategory::TankTrack => "TankTrack",
|
||||
ItemCategory::Rotor => "Rotor",
|
||||
ItemCategory::SprinterLeg => "SprinterLeg",
|
||||
ItemCategory::Propeller => "Propeller",
|
||||
ItemCategory::Laser => "Laser",
|
||||
ItemCategory::Plasma => "Plasma",
|
||||
ItemCategory::Mortar => "Mortar",
|
||||
ItemCategory::Rail => "Rail",
|
||||
ItemCategory::Nano => "Nano",
|
||||
ItemCategory::Tesla => "Tesla",
|
||||
ItemCategory::Aeroflak => "Aeroflak",
|
||||
ItemCategory::Ion => "Ion",
|
||||
ItemCategory::Seeker => "Seeker",
|
||||
ItemCategory::Chaingun => "Chaingun",
|
||||
ItemCategory::ShieldModule => "ShieldModule",
|
||||
ItemCategory::GhostModule => "GhostModule",
|
||||
ItemCategory::BlinkModule => "BlinkModule",
|
||||
ItemCategory::EmpModule => "EmpModule",
|
||||
ItemCategory::WindowmakerModule => "WindowmakerModule",
|
||||
ItemCategory::EnergyModule => "EnergyModule",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn but_bigger(&self) -> i32 {
|
||||
(*self as i32) * 100_000
|
||||
}
|
||||
}
|
||||
29
rc_core/src/data/weapon_upgrade.rs
Normal file
29
rc_core/src/data/weapon_upgrade.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use polariton::{operation::{Dict, Typed}, serdes::TypePrefix};
|
||||
|
||||
use super::{cube_list::ItemTier, weapon_list::ItemCategory};
|
||||
|
||||
pub struct WeaponUpgradeInfo {
|
||||
pub tier: ItemTier,
|
||||
pub type_: ItemCategory,
|
||||
pub xp: f64,
|
||||
pub rating: i32,
|
||||
pub rank: i32,
|
||||
pub power: i32,
|
||||
}
|
||||
|
||||
impl WeaponUpgradeInfo {
|
||||
pub fn as_transmissible<C>(&self) -> Typed<C> {
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str, // str
|
||||
val_ty: TypePrefix::Any, // obj
|
||||
items: vec![
|
||||
(Typed::Str("weaponSize".into()), Typed::Int(self.tier as _)),
|
||||
(Typed::Str("weaponType".into()), Typed::Int(self.type_ as _)),
|
||||
(Typed::Str("weaponXp".into()), Typed::Double(self.xp)),
|
||||
(Typed::Str("weaponRating".into()), Typed::Int(self.rating)),
|
||||
(Typed::Str("weaponRank".into()), Typed::Int(self.rank)),
|
||||
(Typed::Str("weaponPower".into()), Typed::Int(self.power)),
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
8
rc_core/src/lib.rs
Normal file
8
rc_core/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
pub mod data;
|
||||
|
||||
mod state;
|
||||
pub use state::UserState;
|
||||
|
||||
pub mod persist;
|
||||
pub use persist::user::{UserImpl, UserProvider};
|
||||
pub use persist::config::{ConfigImpl, ConfigProvider};
|
||||
202
rc_core/src/persist/combat.rs
Normal file
202
rc_core/src/persist/combat.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct BattleConfig {
|
||||
pub regen: AutoRegenHealth,
|
||||
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)]
|
||||
pub struct AutoRegenHealth {
|
||||
pub wait_for_heal_s: f32,
|
||||
pub wait_full_heal_s: f32,
|
||||
pub sound_start_s: f32,
|
||||
pub auto_heal: bool,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::auto_regen::AutoRegenHealthConfig> for AutoRegenHealth {
|
||||
fn into(self) -> crate::data::auto_regen::AutoRegenHealthConfig {
|
||||
crate::data::auto_regen::AutoRegenHealthConfig {
|
||||
seconds_to_wait_for_heal: self.wait_for_heal_s,
|
||||
seconds_to_full_heal: self.wait_full_heal_s,
|
||||
threshold_to_start_sound: self.sound_start_s,
|
||||
enable_auto_heal: self.auto_heal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct VoteThreshold {
|
||||
pub name: String,
|
||||
pub localised_name: String,
|
||||
pub color: String,
|
||||
pub votes_required: i32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::voting::VoteThresholdData> for VoteThreshold {
|
||||
fn into(self) -> crate::data::voting::VoteThresholdData {
|
||||
crate::data::voting::VoteThresholdData {
|
||||
name: self.name,
|
||||
localised_name: self.localised_name,
|
||||
color: self.color,
|
||||
votes_required: self.votes_required,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Vote {
|
||||
BestPlayed,
|
||||
BestLooking,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::voting::Vote> for Vote {
|
||||
fn into(self) -> crate::data::voting::Vote {
|
||||
match self {
|
||||
Self::BestPlayed => crate::data::voting::Vote::BestPlayed,
|
||||
Self::BestLooking => crate::data::voting::Vote::BestLooking,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
||||
pub struct GameMode {
|
||||
pub respawn_heal_duration: f32,
|
||||
pub respawn_full_heal_duration: f32,
|
||||
pub kill_limit: i32,
|
||||
pub game_time_m: i32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::game_mode::GameModeConfig> for GameMode {
|
||||
fn into(self) -> crate::data::game_mode::GameModeConfig {
|
||||
crate::data::game_mode::GameModeConfig {
|
||||
respawn_heal_duration: self.respawn_heal_duration,
|
||||
respawn_full_heal_duration: self.respawn_full_heal_duration,
|
||||
kill_limit: self.kill_limit,
|
||||
game_time_minutes: self.game_time_m,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Copy)]
|
||||
pub struct GameModes {
|
||||
pub battle_arena: GameMode,
|
||||
pub elimination: GameMode,
|
||||
pub pit: GameMode,
|
||||
pub team_deathmatch: GameMode,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::game_mode::GameModeConfigs> for GameModes {
|
||||
fn into(self) -> crate::data::game_mode::GameModeConfigs {
|
||||
crate::data::game_mode::GameModeConfigs {
|
||||
battle_arena: self.battle_arena.into(),
|
||||
elimination: self.elimination.into(),
|
||||
the_pit: self.pit.into(),
|
||||
team_deathmatch: self.team_deathmatch.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_game_modes() -> GameModes {
|
||||
GameModes {
|
||||
battle_arena: GameMode {
|
||||
respawn_heal_duration: 10.0,
|
||||
respawn_full_heal_duration: 10.0,
|
||||
kill_limit: 0,
|
||||
game_time_m: 20,
|
||||
},
|
||||
elimination: GameMode {
|
||||
respawn_heal_duration: 10.0,
|
||||
respawn_full_heal_duration: 10.0,
|
||||
kill_limit: 10,
|
||||
game_time_m: 10,
|
||||
},
|
||||
pit: GameMode {
|
||||
respawn_heal_duration: 20.0,
|
||||
respawn_full_heal_duration: 20.0,
|
||||
kill_limit: 15,
|
||||
game_time_m: 15,
|
||||
},
|
||||
team_deathmatch: GameMode {
|
||||
respawn_heal_duration: 10.0,
|
||||
respawn_full_heal_duration: 10.0,
|
||||
kill_limit: 10,
|
||||
game_time_m: 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
234
rc_core/src/persist/config/cubes_json.rs
Normal file
234
rc_core/src/persist/config/cubes_json.rs
Normal file
@@ -0,0 +1,234 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use polariton::operation::{Typed, Dict};
|
||||
use polariton::serdes::TypePrefix;
|
||||
|
||||
use super::super::{MovementCategoryData, MovementData, Cube, ItemCategory, ItemTier, BattleConfig};
|
||||
|
||||
const CUBE_CONFIG_FILENAME: &str = "config.json";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CubeConfig {
|
||||
cubes: HashMap<String, Cube>,
|
||||
movement: HashMap<ItemCategory, MovementCategoryData>,
|
||||
lerp_value: f32,
|
||||
battle: BattleConfig,
|
||||
}
|
||||
|
||||
impl CubeConfig {
|
||||
pub fn load(root: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let file = std::fs::File::open(root.as_ref().join(CUBE_CONFIG_FILENAME))?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
|
||||
fn cube_list(&self) -> Typed<C> {
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: self.cubes.values().map(|cube| {
|
||||
let cube_d: crate::data::cube_list::CubeInfo<C> = cube.info.clone().into();
|
||||
cube_d.as_transmissible_key_val(cube.id)
|
||||
}).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
fn movement_list(&self) -> Typed<C> {
|
||||
let mut movements_stats = HashMap::<ItemCategory, HashMap<ItemTier, MovementData>>::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(movement_data) = &cube.movement {
|
||||
let category_map = if let Some(x) = movements_stats.get_mut(&cube.info.category) {
|
||||
x
|
||||
} else {
|
||||
movements_stats.insert(cube.info.category, HashMap::new());
|
||||
movements_stats.get_mut(&cube.info.category).unwrap()
|
||||
};
|
||||
category_map.insert(cube.info.size, movement_data.to_owned());
|
||||
}
|
||||
}
|
||||
let mut movement_cat_stats = Vec::with_capacity(self.movement.len());
|
||||
for (k, v) in self.movement.iter() {
|
||||
let stats: Vec<_> = if let Some(stats) = movements_stats.get(&k) {
|
||||
stats.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect()
|
||||
} else {
|
||||
Vec::default()
|
||||
};
|
||||
let key: crate::data::weapon_list::ItemCategory = k.to_owned().into();
|
||||
let key_typed = Typed::<C>::Str(key.as_str().into());
|
||||
|
||||
let value_data = v.to_owned().into_data(stats);
|
||||
movement_cat_stats.push((key_typed, value_data.as_transmissible()));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: vec![
|
||||
(Typed::Str("Global".into()), Typed::HashMap(vec![
|
||||
(Typed::Str("lerpValue".into()), Typed::Float(self.lerp_value)),
|
||||
].into())),
|
||||
(Typed::Str("Movements".into()), Typed::HashMap(movement_cat_stats.into())),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
fn weapon_list(&self) -> Typed<C> {
|
||||
let mut weapon_stats = HashMap::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(weapon_data) = &cube.weapon {
|
||||
let category_map = if let Some(x) = weapon_stats.get_mut(&cube.info.category) {
|
||||
x
|
||||
} else {
|
||||
weapon_stats.insert(cube.info.category, HashMap::new());
|
||||
weapon_stats.get_mut(&cube.info.category).unwrap()
|
||||
};
|
||||
category_map.insert(cube.info.size, weapon_data.to_owned());
|
||||
}
|
||||
}
|
||||
let mut weapons_vec: Vec<(Typed<C>, Typed<C>)> = Vec::with_capacity(weapon_stats.len());
|
||||
for (k, v) in weapon_stats {
|
||||
let cat_data: crate::data::weapon_list::ItemCategory = k.into();
|
||||
let mut tiers_vec = Vec::with_capacity(v.len());
|
||||
for (k, v) in v {
|
||||
let tier_data: crate::data::cube_list::ItemTier = k.into();
|
||||
let val_data: crate::data::weapon_list::WeaponData = v.into();
|
||||
tiers_vec.push((Typed::Str(tier_data.as_str().into()), val_data.as_transmissible()));
|
||||
}
|
||||
weapons_vec.push((Typed::Str(cat_data.as_str().into()), Typed::HashMap(tiers_vec.into())));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: weapons_vec,
|
||||
})
|
||||
}
|
||||
|
||||
fn weapon_upgrade_list(&self) -> Typed<C> {
|
||||
let mut seen_keys = std::collections::HashSet::new();
|
||||
let mut weapon_upgrades = Vec::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(weapon_up) = &cube.weapon_upgrade {
|
||||
let key = (cube.info.category, cube.info.size);
|
||||
if seen_keys.contains(&key) {
|
||||
log::warn!("Weapon upgrade info for {:?} already exists, skipping", key);
|
||||
} else {
|
||||
seen_keys.insert(key);
|
||||
let weapon_upgrade_data = weapon_up.to_owned().into_data(cube.info.size, cube.info.category);
|
||||
weapon_upgrades.push(weapon_upgrade_data.as_transmissible());
|
||||
}
|
||||
}
|
||||
}
|
||||
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());
|
||||
let mut typed_nodes = Vec::new();
|
||||
for cube in self.cubes.values() {
|
||||
if let Some(tree_data) = &cube.tree {
|
||||
let is_unlocked = unlocked_cubes.contains(&cube.id);
|
||||
let is_unlockable = tree_data.requires.iter().all(|id| unlocked_cubes.contains(id));
|
||||
tree_data.neighbours.iter().for_each(|id| { needed_cubes.insert(*id); });
|
||||
seen_cubes.insert(cube.id);
|
||||
let node_data = tree_data.to_owned().into_data(cube.id, is_unlocked, is_unlockable);
|
||||
typed_nodes.push(node_data.as_transmissible_key_val());
|
||||
}
|
||||
}
|
||||
for needed_cube_id in needed_cubes {
|
||||
if !seen_cubes.contains(&needed_cube_id) {
|
||||
log::warn!("Tech tree needs cube {} but it doesn't have tree info", needed_cube_id);
|
||||
}
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::HashMap,
|
||||
items: typed_nodes,
|
||||
})
|
||||
}
|
||||
|
||||
fn ids(&self) -> Vec<u32> {
|
||||
self.cubes.values().map(|cube| cube.id).collect()
|
||||
}
|
||||
|
||||
fn regen_config(&self) -> Typed<C> {
|
||||
let regen_data: crate::data::auto_regen::AutoRegenHealthConfig = self.battle.regen.clone().into();
|
||||
regen_data.as_transmissible()
|
||||
}
|
||||
|
||||
fn after_battle_vote_config(&self) -> Typed<C> {
|
||||
let mut vote_data = Vec::with_capacity(self.battle.votes.len()); // probably len() == 2
|
||||
for (key, val) in self.battle.votes.iter() {
|
||||
let key_data: crate::data::voting::Vote = key.to_owned().into();
|
||||
let mut val_data = Vec::with_capacity(val.len());
|
||||
for item in val {
|
||||
let vote_data: crate::data::voting::VoteThresholdData = item.to_owned().into();
|
||||
val_data.push(vote_data.as_transmissible());
|
||||
}
|
||||
vote_data.push((Typed::Str(key_data.as_str().into()), Typed::ObjArr(val_data.into())));
|
||||
}
|
||||
Typed::Dict(Dict {
|
||||
key_ty: TypePrefix::Str,
|
||||
val_ty: TypePrefix::Any,
|
||||
items: vote_data,
|
||||
})
|
||||
}
|
||||
|
||||
fn game_mode_config(&self) -> Typed<C> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
13
rc_core/src/persist/config/mod.rs
Normal file
13
rc_core/src/persist/config/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
mod cubes_json;
|
||||
pub use cubes_json::CubeConfig;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{ConfigProvider, CompleteCampaignProvider};
|
||||
|
||||
pub type ConfigImpl = CubeConfig;
|
||||
|
||||
fn __must_impl<T: ConfigProvider<()>>() {}
|
||||
|
||||
fn __test_impl() {
|
||||
__must_impl::<ConfigImpl>();
|
||||
}
|
||||
42
rc_core/src/persist/config/traits.rs
Normal file
42
rc_core/src/persist/config/traits.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use polariton::operation::Typed;
|
||||
|
||||
pub trait ConfigProvider<C> {
|
||||
fn cube_list(&self) -> Typed<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)
|
||||
}
|
||||
}
|
||||
}
|
||||
246
rc_core/src/persist/cube_data.rs
Normal file
246
rc_core/src/persist/cube_data.rs
Normal file
@@ -0,0 +1,246 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use polariton::operation::Typed;
|
||||
|
||||
use super::{WeaponData, WeaponUpgradeInfo, TechTreeData, MovementData};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Cube {
|
||||
pub id: u32,
|
||||
pub info: CubeInfo,
|
||||
pub weapon: Option<WeaponData>,
|
||||
pub weapon_upgrade: Option<WeaponUpgradeInfo>,
|
||||
pub movement: Option<MovementData>,
|
||||
pub tree: Option<TechTreeData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct CubeInfo {
|
||||
#[serde(default = "default_1")]
|
||||
pub cpu: u32,
|
||||
#[serde(default = "default_1")]
|
||||
pub health: u32,
|
||||
#[serde(default)]
|
||||
pub health_boost: f32,
|
||||
#[serde(default)]
|
||||
pub grey_out_in_tutorial: bool,
|
||||
#[serde(default)]
|
||||
pub visibility: VisibilityMode,
|
||||
#[serde(default)]
|
||||
pub indestructible: bool,
|
||||
#[serde(default)]
|
||||
pub category: ItemCategory,
|
||||
#[serde(default = "default_63")]
|
||||
pub placements: u32,
|
||||
#[serde(default)]
|
||||
pub protonium: bool,
|
||||
#[serde(default)]
|
||||
pub unlocked_by_league: bool,
|
||||
#[serde(default)]
|
||||
pub league_unlock_index: i32,
|
||||
pub stats: HashMap<String, serde_json::Value>,
|
||||
pub description: String,
|
||||
pub size: ItemTier,
|
||||
#[serde(rename = "type", alias = "type_")]
|
||||
pub type_: ItemType,
|
||||
#[serde(default)]
|
||||
pub ranking: i32,
|
||||
#[serde(default)]
|
||||
pub cosmetic: bool,
|
||||
#[serde(default)]
|
||||
pub variant_of: u32, // cube id (in hex)
|
||||
#[serde(default = "default_true")]
|
||||
pub ignore_in_weapon_list: bool,
|
||||
}
|
||||
|
||||
fn default_1() -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn default_63() -> u32 {
|
||||
63
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
impl <C: Clone> std::convert::Into<crate::data::cube_list::CubeInfo<C>> for CubeInfo {
|
||||
fn into(self) -> crate::data::cube_list::CubeInfo<C> {
|
||||
crate::data::cube_list::CubeInfo {
|
||||
cpu: self.cpu,
|
||||
health: self.health,
|
||||
health_boost: self.health_boost,
|
||||
grey_out_in_tutorial: self.grey_out_in_tutorial,
|
||||
visibility: self.visibility.into(),
|
||||
indestructible: self.indestructible,
|
||||
category: self.category.into(),
|
||||
placements: self.placements, // default 63
|
||||
protonium: self.protonium,
|
||||
unlocked_by_league: self.unlocked_by_league,
|
||||
league_unlock_index: self.league_unlock_index,
|
||||
stats: self.stats.into_iter().map(|(k, v)| {
|
||||
let new_v = match v {
|
||||
serde_json::Value::Bool(b) => Typed::Bool(b),
|
||||
serde_json::Value::Number(n) => if let Some(n_i64) = n.as_i64() {
|
||||
Typed::Long(n_i64)
|
||||
} else if let Some(n_f64) = n.as_f64() {
|
||||
Typed::Double(n_f64)
|
||||
} else {
|
||||
panic!("Invalid json number")
|
||||
},
|
||||
serde_json::Value::String(s) => Typed::Str(s.into()),
|
||||
_ => panic!("Unsupported stats type"), // TODO is support for Object/Array/Null necessary?
|
||||
};
|
||||
(k, new_v)
|
||||
}).collect(),
|
||||
description: self.description,
|
||||
size: self.size.into(),
|
||||
type_: self.type_.into(),
|
||||
ranking: self.ranking,
|
||||
cosmetic: self.cosmetic,
|
||||
variant_of: hex::encode(self.variant_of.to_be_bytes()).into(),
|
||||
ignore_in_weapon_list: self.ignore_in_weapon_list,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default)]
|
||||
pub enum VisibilityMode {
|
||||
#[default]
|
||||
Mothership,
|
||||
All,
|
||||
Tutorial,
|
||||
None,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::cube_list::VisibilityMode> for VisibilityMode {
|
||||
fn into(self) -> crate::data::cube_list::VisibilityMode {
|
||||
match self {
|
||||
Self::Mothership => crate::data::cube_list::VisibilityMode::Mothership,
|
||||
Self::All => crate::data::cube_list::VisibilityMode::All,
|
||||
Self::Tutorial => crate::data::cube_list::VisibilityMode::Tutorial,
|
||||
Self::None => crate::data::cube_list::VisibilityMode::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
|
||||
pub enum ItemTier {
|
||||
#[default]
|
||||
NoTier = 0,
|
||||
T0 = 100,
|
||||
T1 = 200,
|
||||
T2 = 300,
|
||||
T3 = 400,
|
||||
T4 = 500,
|
||||
T5 = 600,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::cube_list::ItemTier> for ItemTier {
|
||||
fn into(self) -> crate::data::cube_list::ItemTier {
|
||||
match self {
|
||||
Self::NoTier => crate::data::cube_list::ItemTier::NoTier,
|
||||
Self::T0 => crate::data::cube_list::ItemTier::T0,
|
||||
Self::T1 => crate::data::cube_list::ItemTier::T1,
|
||||
Self::T2 => crate::data::cube_list::ItemTier::T2,
|
||||
Self::T3 => crate::data::cube_list::ItemTier::T3,
|
||||
Self::T4 => crate::data::cube_list::ItemTier::T4,
|
||||
Self::T5 => crate::data::cube_list::ItemTier::T5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default)]
|
||||
pub enum ItemType {
|
||||
#[default]
|
||||
NotAFunctionalItem,
|
||||
Weapon,
|
||||
Module,
|
||||
Movement,
|
||||
Cosmetic,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::cube_list::ItemType> for ItemType {
|
||||
fn into(self) -> crate::data::cube_list::ItemType {
|
||||
match self {
|
||||
Self::NotAFunctionalItem => crate::data::cube_list::ItemType::NoFunction,
|
||||
Self::Weapon => crate::data::cube_list::ItemType::Weapon,
|
||||
Self::Module => crate::data::cube_list::ItemType::Module,
|
||||
Self::Movement => crate::data::cube_list::ItemType::Movement,
|
||||
Self::Cosmetic => crate::data::cube_list::ItemType::Cosmetic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
|
||||
pub enum ItemCategory {
|
||||
#[default]
|
||||
NotAFunctionalItem = 0,
|
||||
Wheel = 1,
|
||||
Hover = 2,
|
||||
Wing = 3,
|
||||
Rudder = 4,
|
||||
Thruster = 5,
|
||||
InsectLeg = 6,
|
||||
MechLeg = 7,
|
||||
Ski = 8,
|
||||
TankTrack = 9,
|
||||
Rotor = 10,
|
||||
SprinterLeg = 11,
|
||||
Propeller = 12,
|
||||
Laser = 100,
|
||||
Plasma = 200,
|
||||
Mortar = 250,
|
||||
Rail = 300,
|
||||
Nano = 400,
|
||||
Tesla = 500,
|
||||
Aeroflak = 600,
|
||||
Ion = 650,
|
||||
Seeker = 701,
|
||||
Chaingun = 750,
|
||||
ShieldModule = 800,
|
||||
GhostModule = 801,
|
||||
BlinkModule = 802,
|
||||
EmpModule = 803,
|
||||
WindowmakerModule = 804,
|
||||
EnergyModule = 900,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::weapon_list::ItemCategory> for ItemCategory {
|
||||
fn into(self) -> crate::data::weapon_list::ItemCategory {
|
||||
match self {
|
||||
Self::NotAFunctionalItem => crate::data::weapon_list::ItemCategory::NoFunction,
|
||||
Self::Wheel => crate::data::weapon_list::ItemCategory::Wheel,
|
||||
Self::Hover => crate::data::weapon_list::ItemCategory::Hover,
|
||||
Self::Wing => crate::data::weapon_list::ItemCategory::Wing,
|
||||
Self::Rudder => crate::data::weapon_list::ItemCategory::Rudder,
|
||||
Self::Thruster => crate::data::weapon_list::ItemCategory::Thruster,
|
||||
Self::InsectLeg => crate::data::weapon_list::ItemCategory::InsectLeg,
|
||||
Self::MechLeg => crate::data::weapon_list::ItemCategory::MechLeg,
|
||||
Self::Ski => crate::data::weapon_list::ItemCategory::Ski,
|
||||
Self::TankTrack => crate::data::weapon_list::ItemCategory::TankTrack,
|
||||
Self::Rotor => crate::data::weapon_list::ItemCategory::Rotor,
|
||||
Self::SprinterLeg => crate::data::weapon_list::ItemCategory::SprinterLeg,
|
||||
Self::Propeller => crate::data::weapon_list::ItemCategory::Propeller,
|
||||
Self::Laser => crate::data::weapon_list::ItemCategory::Laser,
|
||||
Self::Plasma => crate::data::weapon_list::ItemCategory::Plasma,
|
||||
Self::Mortar => crate::data::weapon_list::ItemCategory::Mortar,
|
||||
Self::Rail => crate::data::weapon_list::ItemCategory::Rail,
|
||||
Self::Nano => crate::data::weapon_list::ItemCategory::Nano,
|
||||
Self::Tesla => crate::data::weapon_list::ItemCategory::Tesla,
|
||||
Self::Aeroflak => crate::data::weapon_list::ItemCategory::Aeroflak,
|
||||
Self::Ion => crate::data::weapon_list::ItemCategory::Ion,
|
||||
Self::Seeker => crate::data::weapon_list::ItemCategory::Seeker,
|
||||
Self::Chaingun => crate::data::weapon_list::ItemCategory::Chaingun,
|
||||
Self::ShieldModule => crate::data::weapon_list::ItemCategory::ShieldModule,
|
||||
Self::GhostModule => crate::data::weapon_list::ItemCategory::GhostModule,
|
||||
Self::BlinkModule => crate::data::weapon_list::ItemCategory::BlinkModule,
|
||||
Self::EmpModule => crate::data::weapon_list::ItemCategory::EmpModule,
|
||||
Self::WindowmakerModule => crate::data::weapon_list::ItemCategory::WindowmakerModule,
|
||||
Self::EnergyModule => crate::data::weapon_list::ItemCategory::EnergyModule,
|
||||
}
|
||||
}
|
||||
}
|
||||
135
rc_core/src/persist/garage.rs
Normal file
135
rc_core/src/persist/garage.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use super::ItemCategory;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct GarageSlot {
|
||||
#[serde(default)]
|
||||
pub slot: u32,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub cubes: u32,
|
||||
#[serde(default)]
|
||||
pub crf_id: u32, // 0 means not uploaded
|
||||
#[serde(default = "default_false")]
|
||||
pub was_rated: bool,
|
||||
#[serde(default)]
|
||||
pub movement_categories: Vec<ItemCategory>,
|
||||
#[serde(default)]
|
||||
pub uuid: (u32, u32),
|
||||
pub thumbnail_version: u32,
|
||||
#[serde(default)]
|
||||
pub total_robot_cpu: u32,
|
||||
#[serde(default)]
|
||||
pub total_cosmetic_cpu: u32,
|
||||
#[serde(default)]
|
||||
pub total_robot_ranking: u32,
|
||||
#[serde(default)]
|
||||
pub bay_cpu: u32,
|
||||
#[serde(default = "default_false")]
|
||||
pub tutorial_robot: bool,
|
||||
#[serde(default = "default_neg_1")]
|
||||
pub starter_robot_index: i32,
|
||||
#[serde(default)]
|
||||
pub control_type: ControlType,
|
||||
#[serde(default)]
|
||||
pub control_options: GarageControls,
|
||||
#[serde(default)]
|
||||
pub mastery_level: i32,
|
||||
#[serde(default)]
|
||||
pub bay_skin_id: String,
|
||||
#[serde(default)]
|
||||
pub weapon_order: Vec<i32>,
|
||||
#[serde(default = "default_robot_bytes")]
|
||||
pub robot_data: Vec<u8>,
|
||||
#[serde(default = "default_robot_bytes")]
|
||||
pub colour_data: Vec<u8>,
|
||||
}
|
||||
|
||||
fn default_false() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_neg_1() -> i32 {
|
||||
-1
|
||||
}
|
||||
|
||||
fn default_robot_bytes() -> Vec<u8> {
|
||||
vec![0u8, 0u8, 0u8, 0u8]
|
||||
}
|
||||
|
||||
impl GarageSlot {
|
||||
pub fn load(filepath: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
|
||||
let file = std::fs::File::open(filepath)?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn save(&self, filepath: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
let file = std::fs::File::create(filepath)?;
|
||||
let buffered = std::io::BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(buffered, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::garage_bay::GarageSlotInfo> for GarageSlot {
|
||||
fn into(self) -> crate::data::garage_bay::GarageSlotInfo {
|
||||
crate::data::garage_bay::GarageSlotInfo {
|
||||
name: self.name,
|
||||
cubes: self.cubes,
|
||||
crf_id: self.crf_id,
|
||||
was_rated: self.was_rated,
|
||||
movement_categories: self.movement_categories.into_iter().map(|x| x.into()).collect(),
|
||||
uuid: self.uuid,
|
||||
thumbnail_version: self.thumbnail_version,
|
||||
total_robot_cpu: self.total_robot_cpu,
|
||||
total_cosmetic_cpu: self.total_cosmetic_cpu,
|
||||
total_robot_ranking: self.total_robot_ranking,
|
||||
bay_cpu: self.bay_cpu,
|
||||
tutorial_robot: self.tutorial_robot,
|
||||
starter_robot_index: self.starter_robot_index,
|
||||
control_type: self.control_type.into(),
|
||||
control_options: self.control_options.into(),
|
||||
mastery_level: self.mastery_level,
|
||||
bay_skin_id: self.bay_skin_id,
|
||||
weapon_order: self.weapon_order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Default)]
|
||||
pub enum ControlType {
|
||||
#[default]
|
||||
Camera,
|
||||
Keyboard,
|
||||
Count,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::garage_bay::ControlType> for ControlType {
|
||||
fn into(self) -> crate::data::garage_bay::ControlType {
|
||||
match self {
|
||||
Self::Camera => crate::data::garage_bay::ControlType::Camera,
|
||||
Self::Keyboard => crate::data::garage_bay::ControlType::Keyboard,
|
||||
Self::Count => crate::data::garage_bay::ControlType::Count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct GarageControls {
|
||||
pub vertical_strafing: bool,
|
||||
pub sideways_driving: bool,
|
||||
pub tracks_turn_on_spot: bool,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::garage_bay::ControlOptions> for GarageControls {
|
||||
fn into(self) -> crate::data::garage_bay::ControlOptions {
|
||||
crate::data::garage_bay::ControlOptions {
|
||||
vertical_strafing: self.vertical_strafing,
|
||||
sideways_driving: self.sideways_driving,
|
||||
tracks_turn_on_spot: self.tracks_turn_on_spot,
|
||||
}
|
||||
}
|
||||
}
|
||||
804
rc_core/src/persist/mod.rs
Normal file
804
rc_core/src/persist/mod.rs
Normal file
@@ -0,0 +1,804 @@
|
||||
pub mod config;
|
||||
pub mod user;
|
||||
|
||||
mod cube_data;
|
||||
pub use cube_data::{Cube, ItemTier, ItemCategory};
|
||||
//pub use cube_data::{VisibilityMode, ItemType};
|
||||
|
||||
mod garage;
|
||||
pub use garage::{GarageSlot, GarageControls, ControlType};
|
||||
|
||||
mod movement;
|
||||
pub use movement::{MovementCategoryData, MovementData};
|
||||
|
||||
mod weapon;
|
||||
pub use weapon::{WeaponData, WeaponUpgradeInfo};
|
||||
|
||||
mod tech_tree;
|
||||
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];
|
||||
456
rc_core/src/persist/movement.rs
Normal file
456
rc_core/src/persist/movement.rs
Normal file
@@ -0,0 +1,456 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use super::ItemTier;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct MovementCategoryData {
|
||||
pub horizontal_top_speed: Option<f32>,
|
||||
pub vertical_top_speed: Option<f32>,
|
||||
pub min_required_items: Option<i32>,
|
||||
pub min_item_modifier: Option<f32>,
|
||||
pub max_hover_height: Option<f32>,
|
||||
pub light_machine_mass: Option<f32>,
|
||||
pub heavy_machine_mass: Option<f32>,
|
||||
#[serde(flatten)]
|
||||
pub specifics: MovementCategorySpecificData,
|
||||
}
|
||||
|
||||
impl MovementCategoryData {
|
||||
pub fn into_data(self, stats: Vec<(ItemTier, MovementData)>) -> crate::data::movement_list::MovementCategoryData {
|
||||
crate::data::movement_list::MovementCategoryData {
|
||||
horizontal_top_speed: self.horizontal_top_speed,
|
||||
vertical_top_speed: self.vertical_top_speed,
|
||||
min_required_items: self.min_required_items,
|
||||
min_item_modifier: self.min_item_modifier,
|
||||
max_hover_height: self.max_hover_height,
|
||||
light_machine_mass: self.light_machine_mass,
|
||||
heavy_machine_mass: self.heavy_machine_mass,
|
||||
specifics: self.specifics.into(),
|
||||
stats: stats.into_iter().map(|(t, m)| (t.into(), m.into())).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[serde(tag = "movement_enum_variant")]
|
||||
pub enum MovementCategorySpecificData {
|
||||
#[default]
|
||||
Wheel,
|
||||
Hover(HoverCategoryData),
|
||||
Wing,
|
||||
Rudder, // same as wing
|
||||
Thruster,
|
||||
Propeller, // same as thruster
|
||||
InsectLeg,
|
||||
MechLeg(MechLegCategoryData),
|
||||
SprinterLeg(MechLegCategoryData), // same as mech leg
|
||||
TankTrack,
|
||||
Rotor(RotorCategoryData),
|
||||
Ski,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::MovementCategorySpecificData> for MovementCategorySpecificData {
|
||||
fn into(self) -> crate::data::movement_list::MovementCategorySpecificData {
|
||||
match self {
|
||||
Self::Wheel => crate::data::movement_list::MovementCategorySpecificData::Wheel,
|
||||
Self::Hover(x) => crate::data::movement_list::MovementCategorySpecificData::Hover(x.into()),
|
||||
Self::Wing => crate::data::movement_list::MovementCategorySpecificData::Wing,
|
||||
Self::Rudder => crate::data::movement_list::MovementCategorySpecificData::Rudder,
|
||||
Self::Thruster => crate::data::movement_list::MovementCategorySpecificData::Thruster,
|
||||
Self::Propeller => crate::data::movement_list::MovementCategorySpecificData::Propeller,
|
||||
Self::InsectLeg => crate::data::movement_list::MovementCategorySpecificData::InsectLeg,
|
||||
Self::MechLeg(x) => crate::data::movement_list::MovementCategorySpecificData::MechLeg(x.into()),
|
||||
Self::SprinterLeg(x) => crate::data::movement_list::MovementCategorySpecificData::SprinterLeg(x.into()),
|
||||
Self::TankTrack => crate::data::movement_list::MovementCategorySpecificData::TankTrack,
|
||||
Self::Rotor(x) => crate::data::movement_list::MovementCategorySpecificData::Rotor(x.into()),
|
||||
Self::Ski => crate::data::movement_list::MovementCategorySpecificData::Ski,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct HoverCategoryData {
|
||||
pub height_tolerance: f32,
|
||||
pub force_y_offset: f32,
|
||||
pub turning_scale: f32,
|
||||
pub small_angle_turning_scale: f32,
|
||||
pub max_vertical_velocity: f32,
|
||||
pub hover_damping: f32,
|
||||
pub angular_damping: f32,
|
||||
pub deceleration_multiplier: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::HoverCategoryData> for HoverCategoryData {
|
||||
fn into(self) -> crate::data::movement_list::HoverCategoryData {
|
||||
crate::data::movement_list::HoverCategoryData {
|
||||
height_tolerance: self.height_tolerance,
|
||||
force_y_offset: self.force_y_offset,
|
||||
turning_scale: self.turning_scale,
|
||||
small_angle_turning_scale: self.small_angle_turning_scale,
|
||||
max_vertical_velocity: self.max_vertical_velocity,
|
||||
hover_damping: self.hover_damping,
|
||||
angular_damping: self.angular_damping,
|
||||
deceleration_multiplier: self.deceleration_multiplier,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct MechLegCategoryData {
|
||||
pub deceleration_multiplier: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::MechLegCategoryData> for MechLegCategoryData {
|
||||
fn into(self) -> crate::data::movement_list::MechLegCategoryData {
|
||||
crate::data::movement_list::MechLegCategoryData {
|
||||
deceleration_multiplier: self.deceleration_multiplier,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct RotorCategoryData {
|
||||
pub max_turn_rate: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::RotorCategoryData> for RotorCategoryData {
|
||||
fn into(self) -> crate::data::movement_list::RotorCategoryData {
|
||||
crate::data::movement_list::RotorCategoryData {
|
||||
max_turn_rate: self.max_turn_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct MovementData {
|
||||
pub speed_boost: Option<f32>,
|
||||
pub max_carry_mass: Option<f32>,
|
||||
pub horizontal_top_speed: Option<f32>,
|
||||
pub vertical_top_speed: Option<f32>,
|
||||
#[serde(flatten)]
|
||||
pub specifics: MovementSpecificData,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::MovementData> for MovementData {
|
||||
fn into(self) -> crate::data::movement_list::MovementData {
|
||||
crate::data::movement_list::MovementData {
|
||||
speed_boost: self.speed_boost,
|
||||
max_carry_mass: self.max_carry_mass,
|
||||
horizontal_top_speed: self.horizontal_top_speed,
|
||||
vertical_top_speed: self.vertical_top_speed,
|
||||
specifics: self.specifics.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(tag = "movement_enum_variant")]
|
||||
pub enum MovementSpecificData {
|
||||
Wheel(WheelData),
|
||||
Hover(HoverData),
|
||||
Wing(AerofoilData),
|
||||
Rudder(AerofoilData), // same as wing
|
||||
Thruster(ThrusterData),
|
||||
Propeller(ThrusterData), // same as thruster
|
||||
InsectLeg(InsectLegData),
|
||||
MechLeg(MechLegData),
|
||||
SprinterLeg(MechLegData), // same as mech leg
|
||||
TankTrack(TankTrackData),
|
||||
Rotor(RotorData),
|
||||
Ski,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::MovementSpecificData> for MovementSpecificData {
|
||||
fn into(self) -> crate::data::movement_list::MovementSpecificData {
|
||||
match self {
|
||||
Self::Wheel(x) => crate::data::movement_list::MovementSpecificData::Wheel(x.into()),
|
||||
Self::Hover(x) => crate::data::movement_list::MovementSpecificData::Hover(x.into()),
|
||||
Self::Wing(x) => crate::data::movement_list::MovementSpecificData::Wing(x.into()),
|
||||
Self::Rudder(x) => crate::data::movement_list::MovementSpecificData::Rudder(x.into()),
|
||||
Self::Thruster(x) => crate::data::movement_list::MovementSpecificData::Thruster(x.into()),
|
||||
Self::Propeller(x) => crate::data::movement_list::MovementSpecificData::Propeller(x.into()),
|
||||
Self::InsectLeg(x) => crate::data::movement_list::MovementSpecificData::InsectLeg(x.into()),
|
||||
Self::MechLeg(x) => crate::data::movement_list::MovementSpecificData::MechLeg(x.into()),
|
||||
Self::SprinterLeg(x) => crate::data::movement_list::MovementSpecificData::SprinterLeg(x.into()),
|
||||
Self::TankTrack(x) => crate::data::movement_list::MovementSpecificData::TankTrack(x.into()),
|
||||
Self::Rotor(x) => crate::data::movement_list::MovementSpecificData::Rotor(x.into()),
|
||||
Self::Ski=> crate::data::movement_list::MovementSpecificData::Ski,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct WheelData {
|
||||
pub steering_speed_light: f32,
|
||||
pub steering_speed_heavy: f32,
|
||||
pub steering_force_multiplier_light: f32,
|
||||
pub steering_force_multiplier_heavy: f32,
|
||||
pub lateral_acceleration_light: f32,
|
||||
pub lateral_acceleration_heavy: f32,
|
||||
pub time_to_max_acceleration_light: f32,
|
||||
pub time_to_max_acceleration_heavy: f32,
|
||||
pub brake_force_light: f32,
|
||||
pub brake_force_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::WheelData> for WheelData {
|
||||
fn into(self) -> crate::data::movement_list::WheelData {
|
||||
crate::data::movement_list::WheelData {
|
||||
steering_speed_light: self.steering_speed_light,
|
||||
steering_speed_heavy: self.steering_speed_heavy,
|
||||
steering_force_multiplier_light: self.steering_force_multiplier_light,
|
||||
steering_force_multiplier_heavy: self.steering_force_multiplier_heavy,
|
||||
lateral_acceleration_light: self.lateral_acceleration_light,
|
||||
lateral_acceleration_heavy: self.lateral_acceleration_heavy,
|
||||
time_to_max_acceleration_light: self.time_to_max_acceleration_light,
|
||||
time_to_max_acceleration_heavy: self.time_to_max_acceleration_heavy,
|
||||
brake_force_light: self.brake_force_light,
|
||||
brake_force_heavy: self.brake_force_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct HoverData {
|
||||
pub max_hover_height_light: f32,
|
||||
pub max_hover_height_heavy: f32,
|
||||
pub height_change_speed_light: f32,
|
||||
pub height_change_speed_heavy: f32,
|
||||
pub turn_torque_light: f32,
|
||||
pub turn_torque_heavy: f32,
|
||||
pub acceleration_light: f32,
|
||||
pub acceleration_heavy: f32,
|
||||
pub max_angular_velocity_light: f32,
|
||||
pub max_angular_velocity_heavy: f32,
|
||||
pub lateral_damping_light: f32,
|
||||
pub lateral_damping_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::HoverData> for HoverData {
|
||||
fn into(self) -> crate::data::movement_list::HoverData {
|
||||
crate::data::movement_list::HoverData {
|
||||
max_hover_height_light: self.max_hover_height_light,
|
||||
max_hover_height_heavy: self.max_hover_height_heavy,
|
||||
height_change_speed_light: self.height_change_speed_light,
|
||||
height_change_speed_heavy: self.height_change_speed_heavy,
|
||||
turn_torque_light: self.turn_torque_light,
|
||||
turn_torque_heavy: self.turn_torque_heavy,
|
||||
acceleration_light: self.acceleration_light,
|
||||
acceleration_heavy: self.acceleration_heavy,
|
||||
max_angular_velocity_light: self.max_angular_velocity_light,
|
||||
max_angular_velocity_heavy: self.max_angular_velocity_heavy,
|
||||
lateral_damping_light: self.lateral_damping_light,
|
||||
lateral_damping_heavy: self.lateral_damping_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct AerofoilData {
|
||||
pub barrel_speed_light: f32,
|
||||
pub barrel_speed_heavy: f32,
|
||||
pub bank_speed_light: f32,
|
||||
pub bank_speed_heavy: f32,
|
||||
pub elevation_speed_light: f32,
|
||||
pub elevation_speed_heavy: f32,
|
||||
pub rudder_speed_light: f32,
|
||||
pub rudder_speed_heavy: f32,
|
||||
pub thrust_light: f32,
|
||||
pub thrust_heavy: f32,
|
||||
pub vtol_velocity_light: f32,
|
||||
pub vtol_velocity_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::AerofoilData> for AerofoilData {
|
||||
fn into(self) -> crate::data::movement_list::AerofoilData {
|
||||
crate::data::movement_list::AerofoilData {
|
||||
barrel_speed_light: self.barrel_speed_light,
|
||||
barrel_speed_heavy: self.barrel_speed_heavy,
|
||||
bank_speed_light: self.bank_speed_light,
|
||||
bank_speed_heavy: self.bank_speed_heavy,
|
||||
elevation_speed_light: self.elevation_speed_light,
|
||||
elevation_speed_heavy: self.elevation_speed_heavy,
|
||||
rudder_speed_light: self.rudder_speed_light,
|
||||
rudder_speed_heavy: self.rudder_speed_heavy,
|
||||
thrust_light: self.thrust_light,
|
||||
thrust_heavy: self.thrust_heavy,
|
||||
vtol_velocity_light: self.vtol_velocity_light,
|
||||
vtol_velocity_heavy: self.vtol_velocity_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct ThrusterData {
|
||||
pub acceleration_delay_light: f32,
|
||||
pub acceleration_delay_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::ThrusterData> for ThrusterData {
|
||||
fn into(self) -> crate::data::movement_list::ThrusterData {
|
||||
crate::data::movement_list::ThrusterData {
|
||||
acceleration_delay_light: self.acceleration_delay_light,
|
||||
acceleration_delay_heavy: self.acceleration_delay_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct InsectLegData {
|
||||
pub ideal_height_light: f32,
|
||||
pub ideal_height_heavy: f32,
|
||||
pub ideal_crouching_height_light: f32,
|
||||
pub ideal_crouching_height_heavy: f32,
|
||||
pub ideal_height_range_light: f32,
|
||||
pub ideal_height_range_heavy: f32,
|
||||
pub jump_height_light: f32,
|
||||
pub jump_height_heavy: f32,
|
||||
pub max_upwards_force_light: f32,
|
||||
pub max_upwards_force_heavy: f32,
|
||||
pub max_lateral_force_light: f32,
|
||||
pub max_lateral_force_heavy: f32,
|
||||
pub max_turning_force_light: f32,
|
||||
pub max_turning_force_heavy: f32,
|
||||
pub max_damping_force_light: f32,
|
||||
pub max_damping_force_heavy: f32,
|
||||
pub max_stopped_force_light: f32,
|
||||
pub max_stopped_force_heavy: f32,
|
||||
pub max_new_stopped_force_light: f32,
|
||||
pub max_new_stopped_force_heavy: f32,
|
||||
pub upwards_damping_force_light: f32,
|
||||
pub upwards_damping_force_heavy: f32,
|
||||
pub lateral_damp_force_light: f32,
|
||||
pub lateral_damp_force_heavy: f32,
|
||||
pub swagger_force_light: f32,
|
||||
pub swagger_force_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::InsectLegData> for InsectLegData {
|
||||
fn into(self) -> crate::data::movement_list::InsectLegData {
|
||||
crate::data::movement_list::InsectLegData {
|
||||
ideal_height_light: self.ideal_height_light,
|
||||
ideal_height_heavy: self.ideal_height_heavy,
|
||||
ideal_crouching_height_light: self.ideal_crouching_height_light,
|
||||
ideal_crouching_height_heavy: self.ideal_crouching_height_heavy,
|
||||
ideal_height_range_light: self.ideal_height_range_light,
|
||||
ideal_height_range_heavy: self.ideal_height_range_heavy,
|
||||
jump_height_light: self.jump_height_light,
|
||||
jump_height_heavy: self.jump_height_heavy,
|
||||
max_upwards_force_light: self.max_upwards_force_light,
|
||||
max_upwards_force_heavy: self.max_upwards_force_heavy,
|
||||
max_lateral_force_light: self.max_lateral_force_light,
|
||||
max_lateral_force_heavy: self.max_lateral_force_heavy,
|
||||
max_turning_force_light: self.max_turning_force_light,
|
||||
max_turning_force_heavy: self.max_turning_force_heavy,
|
||||
max_damping_force_light: self.max_damping_force_light,
|
||||
max_damping_force_heavy: self.max_damping_force_heavy,
|
||||
max_stopped_force_light: self.max_stopped_force_light,
|
||||
max_stopped_force_heavy: self.max_stopped_force_heavy,
|
||||
max_new_stopped_force_light: self.max_new_stopped_force_light,
|
||||
max_new_stopped_force_heavy: self.max_new_stopped_force_heavy,
|
||||
upwards_damping_force_light: self.upwards_damping_force_light,
|
||||
upwards_damping_force_heavy: self.upwards_damping_force_heavy,
|
||||
lateral_damp_force_light: self.lateral_damp_force_light,
|
||||
lateral_damp_force_heavy: self.lateral_damp_force_heavy,
|
||||
swagger_force_light: self.swagger_force_light,
|
||||
swagger_force_heavy: self.swagger_force_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct MechLegData {
|
||||
pub time_grounded_after_jump_light: f32,
|
||||
pub time_grounded_after_jump_heavy: f32,
|
||||
pub jump_height_light: f32,
|
||||
pub jump_height_heavy: f32,
|
||||
pub turn_acceleration_light: f32,
|
||||
pub turn_acceleration_heavy: f32,
|
||||
pub legacy_turn_acceleration_light: f32,
|
||||
pub legacy_turn_acceleration_heavy: f32,
|
||||
pub long_jump_speed_scale_light: f32,
|
||||
pub long_jump_speed_scale_heavy: f32,
|
||||
pub max_lateral_force_light: f32,
|
||||
pub max_lateral_force_heavy: f32,
|
||||
pub max_damping_force_light: f32,
|
||||
pub max_damping_force_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::MechLegData> for MechLegData {
|
||||
fn into(self) -> crate::data::movement_list::MechLegData {
|
||||
crate::data::movement_list::MechLegData {
|
||||
time_grounded_after_jump_light: self.time_grounded_after_jump_light,
|
||||
time_grounded_after_jump_heavy: self.time_grounded_after_jump_heavy,
|
||||
jump_height_light: self.jump_height_light,
|
||||
jump_height_heavy: self.jump_height_heavy,
|
||||
turn_acceleration_light: self.turn_acceleration_light,
|
||||
turn_acceleration_heavy: self.turn_acceleration_heavy,
|
||||
legacy_turn_acceleration_light: self.legacy_turn_acceleration_light,
|
||||
legacy_turn_acceleration_heavy: self.legacy_turn_acceleration_heavy,
|
||||
long_jump_speed_scale_light: self.long_jump_speed_scale_light,
|
||||
long_jump_speed_scale_heavy: self.long_jump_speed_scale_heavy,
|
||||
max_lateral_force_light: self.max_lateral_force_light,
|
||||
max_lateral_force_heavy: self.max_lateral_force_heavy,
|
||||
max_damping_force_light: self.max_damping_force_light,
|
||||
max_damping_force_heavy: self.max_damping_force_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct TankTrackData {
|
||||
pub max_turn_rate_moving_light: f32,
|
||||
pub max_turn_rate_moving_heavy: f32,
|
||||
pub max_turn_rate_stopped_light: f32,
|
||||
pub max_turn_rate_stopped_heavy: f32,
|
||||
pub turn_acceleration_light: f32,
|
||||
pub turn_acceleration_heavy: f32,
|
||||
pub lateral_acceleration_light: f32,
|
||||
pub lateral_acceleration_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::TankTrackData> for TankTrackData {
|
||||
fn into(self) -> crate::data::movement_list::TankTrackData {
|
||||
crate::data::movement_list::TankTrackData {
|
||||
max_turn_rate_moving_light: self.max_turn_rate_moving_light,
|
||||
max_turn_rate_moving_heavy: self.max_turn_rate_moving_heavy,
|
||||
max_turn_rate_stopped_light: self.max_turn_rate_stopped_light,
|
||||
max_turn_rate_stopped_heavy: self.max_turn_rate_stopped_heavy,
|
||||
turn_acceleration_light: self.turn_acceleration_light,
|
||||
turn_acceleration_heavy: self.turn_acceleration_heavy,
|
||||
lateral_acceleration_light: self.lateral_acceleration_light,
|
||||
lateral_acceleration_heavy: self.lateral_acceleration_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct RotorData {
|
||||
pub height_acceleration_light: f32,
|
||||
pub height_acceleration_heavy: f32,
|
||||
pub strafe_acceleration_light: f32,
|
||||
pub strafe_acceleration_heavy: f32,
|
||||
pub turn_acceleration_light: f32,
|
||||
pub turn_acceleration_heavy: f32,
|
||||
pub height_max_change_speed_light: f32,
|
||||
pub height_max_change_speed_heavy: f32,
|
||||
pub level_acceleration_light: f32,
|
||||
pub level_acceleration_heavy: f32,
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::movement_list::RotorData> for RotorData {
|
||||
fn into(self) -> crate::data::movement_list::RotorData {
|
||||
crate::data::movement_list::RotorData {
|
||||
height_acceleration_light: self.height_acceleration_light,
|
||||
height_acceleration_heavy: self.height_acceleration_heavy,
|
||||
strafe_acceleration_light: self.strafe_acceleration_light,
|
||||
strafe_acceleration_heavy: self.strafe_acceleration_heavy,
|
||||
turn_acceleration_light: self.turn_acceleration_light,
|
||||
turn_acceleration_heavy: self.turn_acceleration_heavy,
|
||||
height_max_change_speed_light: self.height_max_change_speed_light,
|
||||
height_max_change_speed_heavy: self.height_max_change_speed_heavy,
|
||||
level_acceleration_light: self.level_acceleration_light,
|
||||
level_acceleration_heavy: self.level_acceleration_heavy,
|
||||
}
|
||||
}
|
||||
}
|
||||
224
rc_core/src/persist/singleplayer.rs
Normal file
224
rc_core/src/persist/singleplayer.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
25
rc_core/src/persist/tech_tree.rs
Normal file
25
rc_core/src/persist/tech_tree.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[serde(default)]
|
||||
pub struct TechTreeData {
|
||||
pub position_x: i32,
|
||||
pub position_y: i32,
|
||||
pub tech_points: u32,
|
||||
pub neighbours: Vec<u32>, // cube IDs
|
||||
pub requires: Vec<u32>,
|
||||
}
|
||||
|
||||
impl TechTreeData {
|
||||
pub fn into_data(self, self_id: u32, self_is_unlocked: bool, self_is_unlockable: bool) -> crate::data::tech_tree::TechTreeNode {
|
||||
crate::data::tech_tree::TechTreeNode {
|
||||
main_cube_id: self_id as i32,
|
||||
position_x: self.position_x,
|
||||
position_y: self.position_y,
|
||||
is_unlocked: self_is_unlocked,
|
||||
is_unlockable: self_is_unlockable,
|
||||
tech_points: self.tech_points,
|
||||
neighbours: self.neighbours.into_iter().map(|x| x as i32).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
261
rc_core/src/persist/user/account_json.rs
Normal file
261
rc_core/src/persist/user/account_json.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::persist::config::ConfigProvider;
|
||||
|
||||
pub struct AccountProvider {
|
||||
root: std::path::PathBuf,
|
||||
cubes: std::sync::Arc<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl AccountProvider {
|
||||
pub fn load(root: impl AsRef<std::path::Path>, cubes: &crate::persist::config::ConfigImpl) -> std::io::Result<Self> {
|
||||
let root = root.as_ref().join(super::USERS_DIR);
|
||||
std::fs::create_dir_all(&root)?;
|
||||
Ok(Self {
|
||||
root,
|
||||
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(cubes)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl <C: Clone> super::UserProvider<C> for AccountProvider {
|
||||
fn authenticate(&self, token: super::UserToken) -> Result<Box<dyn super::User<C> + Send + Sync>, String> {
|
||||
let new_root = self.root.join(&token.uuid);
|
||||
if !new_root.exists() {
|
||||
std::fs::create_dir(&new_root).map_err(|e| e.to_string())?;
|
||||
log::info!("New user {}", token.uuid);
|
||||
super::setup_directory(&new_root).map_err(|e| e.to_string())?;
|
||||
}
|
||||
let account_info = AccountInfo::load(&new_root).map_err(|e| e.to_string())?;
|
||||
Ok(Box::new(UserData {
|
||||
root: new_root,
|
||||
token,
|
||||
account: account_info,
|
||||
cubes: self.cubes.clone(),
|
||||
}))
|
||||
//Err("Unable to authenticate".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct UserData {
|
||||
root: std::path::PathBuf,
|
||||
token: super::UserToken,
|
||||
account: AccountInfo,
|
||||
cubes: std::sync::Arc<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl UserData {
|
||||
fn load_garage_by_id(&self, id: u32) -> std::io::Result<crate::persist::GarageSlot> {
|
||||
let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", id));
|
||||
crate::persist::GarageSlot::load(&path)
|
||||
}
|
||||
|
||||
fn save_garage(&self, slot: &crate::persist::GarageSlot) -> std::io::Result<()> {
|
||||
let path = self.root.join(super::GARAGE_DIR).join(format!("{}.json", slot.slot));
|
||||
slot.save(path)
|
||||
}
|
||||
|
||||
fn all_vehicles(&self) -> std::io::Result<Vec<crate::persist::GarageSlot>> {
|
||||
let path = self.root.join(super::GARAGE_DIR);
|
||||
let mut slots = Vec::new();
|
||||
for entry in std::fs::read_dir(path)? {
|
||||
let entry = entry?;
|
||||
let filepath = entry.path();
|
||||
if filepath.is_file() {
|
||||
let slot = crate::persist::GarageSlot::load(&filepath)?;
|
||||
slots.push(slot);
|
||||
} else {
|
||||
log::warn!("Ignoring non-file {} in {} dir", filepath.display(), super::GARAGE_DIR);
|
||||
}
|
||||
}
|
||||
slots.sort_by_key(|slot| slot.slot);
|
||||
Ok(slots)
|
||||
}
|
||||
}
|
||||
|
||||
const INVALID_ROBOT_ERR: i16 = 140;
|
||||
const DATABASE_ERR: i16 = 8;
|
||||
|
||||
impl <C: Clone> super::User<C> for UserData {
|
||||
fn token(&self) -> &'_ super::UserToken {
|
||||
&self.token
|
||||
}
|
||||
|
||||
fn is_mod(&self) -> bool {
|
||||
self.account.is_mod
|
||||
}
|
||||
|
||||
fn is_admin(&self) -> bool {
|
||||
self.account.is_admin
|
||||
}
|
||||
|
||||
fn is_dev(&self) -> bool {
|
||||
self.account.is_dev
|
||||
}
|
||||
|
||||
fn unlocked_parts(&self) -> Vec<u32> {
|
||||
match self.account.inventory.override_ {
|
||||
super::inventory::UnlockOverride::Normal => self.account.inventory.unlocked.clone(),
|
||||
super::inventory::UnlockOverride::UnlockNone => Vec::default(),
|
||||
super::inventory::UnlockOverride::UnlockAll => self.cubes.as_ref().to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn selected_garage_uuid(&self) -> String {
|
||||
self.account.garage.uuid_str()
|
||||
}
|
||||
|
||||
fn selected_garage_slot(&self) -> u32 {
|
||||
self.account.garage.slot
|
||||
}
|
||||
|
||||
fn all_slots_by_id(&self) -> super::UserSlots<C> {
|
||||
let slots = match self.all_vehicles() {
|
||||
Ok(slots) => slots,
|
||||
Err(e) => {
|
||||
log::error!("Failed to load all vehicles: {}", e);
|
||||
Vec::default()
|
||||
}
|
||||
};
|
||||
let slot_order = polariton::operation::Typed::ObjArr(slots.iter().map(|slot| polariton::operation::Typed::Int(slot.slot as _)).collect::<Vec<_>>().into());
|
||||
let slot_info = polariton::operation::Typed::Dict(polariton::operation::Dict {
|
||||
key_ty: polariton::serdes::TypePrefix::Int,
|
||||
val_ty: polariton::serdes:: TypePrefix::HashMap,
|
||||
items: slots.into_iter().map(|slot| {
|
||||
let slot_index = slot.slot;
|
||||
let garage_data: crate::data::garage_bay::GarageSlotInfo = slot.into();
|
||||
(polariton::operation::Typed::Int(slot_index as _), garage_data.as_transmissible())
|
||||
}).collect(),
|
||||
});
|
||||
super::UserSlots {
|
||||
slot_info, slot_order,
|
||||
}
|
||||
}
|
||||
|
||||
fn slot_by_id(&self, id: i32) -> Result<crate::persist::user::UserSlotData<C>, i16> {
|
||||
match self.load_garage_by_id(id as _) {
|
||||
Ok(slot) => {
|
||||
let control_ty: crate::data::garage_bay::ControlType = slot.control_type.into();
|
||||
let control_options: crate::data::garage_bay::ControlOptions = slot.control_options.into();
|
||||
Ok(crate::persist::user::UserSlotData {
|
||||
data: polariton::operation::Typed::Bytes(slot.robot_data.into()),
|
||||
colour_data: polariton::operation::Typed::Bytes(slot.colour_data.into()),
|
||||
cube_count: polariton::operation::Typed::Int(slot.cubes as _),
|
||||
weapon_order: polariton::operation::Typed::IntArr(slot.weapon_order.clone().into()),
|
||||
movement_categories: polariton::operation::Typed::IntArr(slot.movement_categories.into_iter().map(|cat| {
|
||||
let cat: crate::data::weapon_list::ItemCategory = cat.into();
|
||||
cat.but_bigger()
|
||||
}).collect::<Vec<_>>().into()),
|
||||
control_type: polariton::operation::Typed::Int(control_ty as _),
|
||||
control_options: control_options.as_transmissible(),
|
||||
mastery_level: polariton::operation::Typed::Int(0), // TODO
|
||||
robot_rank: polariton::operation::Typed::Int(slot.total_robot_ranking as _),
|
||||
cpu: polariton::operation::Typed::Int(slot.total_robot_cpu as _),
|
||||
cosmetic_cpu: polariton::operation::Typed::Int(slot.total_cosmetic_cpu as _),
|
||||
uuid: polariton::operation::Typed::Str(format!("{}_{}", slot.uuid.0, slot.uuid.1).into()),
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to load vehicle {}: {}", id, e);
|
||||
Err(INVALID_ROBOT_ERR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_slot(&self, vehicle: crate::persist::user::VehicleData) -> Result<(), i16> {
|
||||
let id = vehicle.id as u32;
|
||||
let mut existing_data = self.load_garage_by_id(id).map_err(|e| {
|
||||
log::error!("Failed to load vehicle {}: {}", id, e);
|
||||
INVALID_ROBOT_ERR
|
||||
})?;
|
||||
existing_data.slot = id;
|
||||
existing_data.robot_data = vehicle.robot_data;
|
||||
existing_data.colour_data = vehicle.colour_data;
|
||||
log::debug!("weapon order: {:?}", vehicle.weapon_order);
|
||||
existing_data.weapon_order = vehicle.weapon_order;
|
||||
self.save_garage(&existing_data).map_err(|e| {
|
||||
log::error!("Failed to save vehicle {}: {}", id, e);
|
||||
DATABASE_ERR
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn signup_date(&self) -> i64 {
|
||||
match self.root.metadata() {
|
||||
Ok(meta) => {
|
||||
match meta.created() {
|
||||
Ok(created) => {
|
||||
match created.duration_since(std::time::SystemTime::UNIX_EPOCH) {
|
||||
Ok(dur) => {
|
||||
return super::since_windows_epoch(dur.as_secs() as i64);
|
||||
},
|
||||
Err(e) => log::error!("could not get duration since unix epoch of {}: {}", self.root.display(), e),
|
||||
}
|
||||
},
|
||||
Err(e) => log::error!("could not read creation time of {}: {}", self.root.display(), e),
|
||||
}
|
||||
},
|
||||
Err(e) => log::error!("could not retrieve metadata of {}: {}", self.root.display(), e),
|
||||
}
|
||||
super::since_windows_epoch(0)
|
||||
}
|
||||
|
||||
fn singleplayer_robots(&self) -> Result<polariton::operation::Typed<C>, i16> {
|
||||
let current_slot = self.load_garage_by_id(self.account.garage.slot).map_err(|e| {
|
||||
log::error!("Failed to load current vehicle: {}", e);
|
||||
INVALID_ROBOT_ERR
|
||||
})?;
|
||||
let user_uuid = self.token.uuid.clone();
|
||||
Ok(crate::data::player_data::PlayerDatas {
|
||||
players: vec![
|
||||
crate::data::player_data::PlayerData {
|
||||
name: user_uuid.clone(),
|
||||
display_name: user_uuid,
|
||||
mastery: current_slot.mastery_level,
|
||||
tier: 1, // FIXME
|
||||
robot_name: current_slot.name,
|
||||
robot_map: current_slot.robot_data,
|
||||
team: 0,
|
||||
has_premium: true, // FIXME
|
||||
robot_uuid: format!("{}_{}", current_slot.uuid.0, current_slot.uuid.1),
|
||||
cpu: current_slot.total_robot_cpu as i32,
|
||||
weapon_order: current_slot.weapon_order.clone(),
|
||||
colour_map: current_slot.colour_data,
|
||||
is_ai: false,
|
||||
spawn_effect: "Spawn_Warp".to_owned(), // FIXME
|
||||
death_effect: "Explosion_Warp".to_owned(), // FIXME
|
||||
player_rank: 1, // FIXME
|
||||
weapon_rank: current_slot.weapon_order.into_iter().map(|x| (x, if x == 0 { 0 } else { 1 })).collect(),
|
||||
}
|
||||
],
|
||||
}.as_transmissible())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct AccountInfo {
|
||||
pub is_mod: bool,
|
||||
pub is_admin: bool,
|
||||
pub is_dev: bool,
|
||||
pub inventory: super::UnlockedParts,
|
||||
pub garage: super::SelectedGarage,
|
||||
}
|
||||
|
||||
impl AccountInfo {
|
||||
fn load(root: impl AsRef<std::path::Path>) -> std::io::Result<AccountInfo> {
|
||||
let file = std::fs::File::open(root.as_ref().join(super::USER_FILE))?;
|
||||
let buffered = std::io::BufReader::new(file);
|
||||
let result = serde_json::from_reader(buffered)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn save(&self, root: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
let file = std::fs::File::create(root.as_ref().join(super::USER_FILE))?;
|
||||
let buffered = std::io::BufWriter::new(file);
|
||||
serde_json::to_writer_pretty(buffered, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
13
rc_core/src/persist/user/garage_data.rs
Normal file
13
rc_core/src/persist/user/garage_data.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SelectedGarage {
|
||||
pub uuid: (u32, u32),
|
||||
pub slot: u32,
|
||||
}
|
||||
|
||||
impl SelectedGarage {
|
||||
pub fn uuid_str(&self) -> String {
|
||||
format!("{}_{}", self.uuid.0, self.uuid.1)
|
||||
}
|
||||
}
|
||||
88
rc_core/src/persist/user/initial_data.rs
Normal file
88
rc_core/src/persist/user/initial_data.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
const REFERENCE_DIR: &str = "layout folder";
|
||||
|
||||
fn build_reference_directory(root: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
std::fs::create_dir(&root)?;
|
||||
let garage_dir = root.as_ref().join(super::GARAGE_DIR);
|
||||
std::fs::create_dir(&garage_dir)?;
|
||||
default_user_data().save(&root)?;
|
||||
for slot in default_garage_slots() {
|
||||
let filepath = garage_dir.join(format!("{}.json", slot.slot));
|
||||
slot.save(filepath)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup_directory(new_dir: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||
let ref_path = new_dir.as_ref().parent().unwrap().join(REFERENCE_DIR);
|
||||
if !ref_path.exists() {
|
||||
log::debug!("Initialising reference directory {}", ref_path.display());
|
||||
build_reference_directory(&ref_path)?;
|
||||
}
|
||||
log::debug!("Copying reference directory for new user: {}", new_dir.as_ref().display());
|
||||
so::copy_dir_all(ref_path, new_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_user_data() -> super::AccountInfo {
|
||||
super::AccountInfo {
|
||||
is_mod: false,
|
||||
is_admin: false,
|
||||
is_dev: false,
|
||||
inventory: super::UnlockedParts {
|
||||
unlocked: vec![],
|
||||
override_: super::inventory::UnlockOverride::Normal,
|
||||
},
|
||||
garage: super::SelectedGarage {
|
||||
uuid: (0, 0),
|
||||
slot: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn default_garage_slots() -> Vec<crate::persist::GarageSlot> {
|
||||
vec![
|
||||
crate::persist::GarageSlot {
|
||||
slot: 0,
|
||||
name: "Reverse-engineer great success! slot_name".to_owned(),
|
||||
cubes: 1,
|
||||
crf_id: 0,
|
||||
was_rated: false,
|
||||
movement_categories: vec![crate::persist::ItemCategory::Wheel],
|
||||
uuid: (0, 0),
|
||||
thumbnail_version: 0,
|
||||
total_robot_cpu: 1,
|
||||
total_cosmetic_cpu: 0,
|
||||
total_robot_ranking: 1,
|
||||
bay_cpu: 2_000,
|
||||
tutorial_robot: false,
|
||||
starter_robot_index: -1,
|
||||
control_type: crate::persist::ControlType::Camera,
|
||||
control_options: crate::persist::GarageControls { vertical_strafing: false, sideways_driving: false, tracks_turn_on_spot: false, },
|
||||
mastery_level: 1,
|
||||
bay_skin_id: "RC_MothershipSkin_Neptune_01".to_owned(), // TODO get the rest of the names
|
||||
weapon_order: vec![],
|
||||
robot_data: vec![0; 4],
|
||||
colour_data: vec![0; 4],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
mod so {
|
||||
// from https://stackoverflow.com/questions/26958489/how-to-copy-a-folder-recursively-in-rust
|
||||
use std::path::Path;
|
||||
use std::{io, fs};
|
||||
|
||||
pub(super) fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
fs::create_dir_all(&dst)?;
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let ty = entry.file_type()?;
|
||||
if ty.is_dir() {
|
||||
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
16
rc_core/src/persist/user/inventory.rs
Normal file
16
rc_core/src/persist/user/inventory.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct UnlockedParts {
|
||||
pub unlocked: Vec<u32>,
|
||||
#[serde(rename = "override", default)]
|
||||
pub override_: UnlockOverride,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub enum UnlockOverride {
|
||||
#[default]
|
||||
Normal,
|
||||
UnlockAll,
|
||||
UnlockNone,
|
||||
}
|
||||
34
rc_core/src/persist/user/mod.rs
Normal file
34
rc_core/src/persist/user/mod.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
mod account_json;
|
||||
pub use account_json::{AccountProvider, AccountInfo};
|
||||
|
||||
mod garage_data;
|
||||
pub use garage_data::SelectedGarage;
|
||||
|
||||
mod initial_data;
|
||||
pub use initial_data::setup_directory;
|
||||
|
||||
mod inventory;
|
||||
pub use inventory::UnlockedParts;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData};
|
||||
|
||||
pub const USERS_DIR: &str = "accounts";
|
||||
pub const USER_FILE: &str = "user.json";
|
||||
pub const GARAGE_DIR: &str = "vehicles";
|
||||
|
||||
pub type UserImpl = AccountProvider;
|
||||
|
||||
fn __must_impl<T: UserProvider<()>>() {}
|
||||
|
||||
fn __test_impl() {
|
||||
__must_impl::<UserImpl>();
|
||||
}
|
||||
|
||||
pub fn since_windows_epoch(since_unix_epoch: i64) -> i64 {
|
||||
use chrono::TimeZone;
|
||||
let windows_epoch = chrono::Utc.from_utc_datetime(&chrono::NaiveDateTime::parse_from_str("1601-01-01 00:00:00", "%Y-%m-%d %H:%M:%S").unwrap());
|
||||
let time_in = chrono::DateTime::<chrono::Utc>::from_timestamp(since_unix_epoch, 0).unwrap();
|
||||
//let time_in = chrono::Utc.from_utc_datetime(&chrono::NaiveDateTime::from_timestamp(since_unix_epoch, 0));
|
||||
time_in.signed_duration_since(windows_epoch).num_milliseconds() * 10_000
|
||||
}
|
||||
53
rc_core/src/persist/user/traits.rs
Normal file
53
rc_core/src/persist/user/traits.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub struct UserToken {
|
||||
pub uuid: String,
|
||||
pub token: String,
|
||||
pub refresh_token: String,
|
||||
}
|
||||
|
||||
pub trait UserProvider<C> {
|
||||
fn authenticate(&self, user: UserToken) -> Result<Box<dyn User<C> + Send + Sync>, String>;
|
||||
}
|
||||
|
||||
pub trait User<C> {
|
||||
fn token(&self) -> &'_ super::UserToken;
|
||||
fn is_mod(&self) -> bool;
|
||||
fn is_admin(&self) -> bool;
|
||||
fn is_dev(&self) -> bool;
|
||||
fn unlocked_parts(&self) -> Vec<u32>;
|
||||
fn selected_garage_uuid(&self) -> String;
|
||||
fn selected_garage_slot(&self) -> u32;
|
||||
fn all_slots_by_id(&self) -> UserSlots<C>;
|
||||
fn slot_by_id(&self, id: i32) -> Result<UserSlotData<C>, i16>;
|
||||
fn save_slot(&self, vehicle: VehicleData) -> Result<(), i16>;
|
||||
fn signup_date(&self) -> i64;
|
||||
fn singleplayer_robots(&self) -> Result<polariton::operation::Typed<C>, i16>;
|
||||
}
|
||||
|
||||
pub struct UserSlots<C> {
|
||||
pub slot_info: polariton::operation::Typed<C>,
|
||||
pub slot_order: polariton::operation::Typed<C>,
|
||||
}
|
||||
|
||||
pub struct UserSlotData<C> {
|
||||
pub data: polariton::operation::Typed<C>,
|
||||
pub colour_data: polariton::operation::Typed<C>,
|
||||
pub cube_count: polariton::operation::Typed<C>,
|
||||
pub weapon_order: polariton::operation::Typed<C>,
|
||||
pub movement_categories: polariton::operation::Typed<C>,
|
||||
pub control_type: polariton::operation::Typed<C>,
|
||||
pub control_options: polariton::operation::Typed<C>,
|
||||
pub mastery_level: polariton::operation::Typed<C>,
|
||||
pub robot_rank: polariton::operation::Typed<C>,
|
||||
pub cpu: polariton::operation::Typed<C>,
|
||||
pub cosmetic_cpu: polariton::operation::Typed<C>,
|
||||
pub uuid: polariton::operation::Typed<C>,
|
||||
}
|
||||
|
||||
pub struct VehicleData {
|
||||
pub id: i32,
|
||||
pub robot_data: Vec<u8>,
|
||||
pub colour_data: Vec<u8>,
|
||||
pub weapon_order: Vec<i32>,
|
||||
}
|
||||
176
rc_core/src/persist/weapon.rs
Normal file
176
rc_core/src/persist/weapon.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[serde(default)]
|
||||
pub struct WeaponData {
|
||||
pub damage_inflicted: Option<i32>,
|
||||
pub protonium_damage_scale: Option<f32>,
|
||||
pub projectile_speed: Option<f32>,
|
||||
pub projectile_range: Option<f32>,
|
||||
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>,
|
||||
pub plasma_starting_radius_scale: Option<f32>,
|
||||
pub nano_dps: Option<f32>,
|
||||
pub nano_hps: Option<f32>,
|
||||
pub tesla_damage: Option<f32>,
|
||||
pub tesla_charges: Option<f32>,
|
||||
pub aeroflak_proximity_damage: Option<f32>,
|
||||
pub aeroflak_damage_radius: Option<f32>,
|
||||
pub aeroflak_explosion_radius: Option<f32>,
|
||||
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>,
|
||||
pub spin_down_time: Option<f32>,
|
||||
pub spin_initial_cooldown: Option<f32>,
|
||||
#[serde(default = "group_fire_scales_default")]
|
||||
pub group_fire_scales: Vec<f32>,
|
||||
pub mana_cost: Option<f32>,
|
||||
pub lock_time: Option<f32>,
|
||||
pub full_lock_release: Option<f32>,
|
||||
pub change_lock_time: Option<f32>,
|
||||
pub max_rotation_speed: Option<f32>,
|
||||
pub initial_rotation_speed: Option<f32>,
|
||||
pub rotation_acceleration: Option<f32>,
|
||||
pub nano_healing_priority_time: Option<f32>,
|
||||
pub module_range: Option<f32>,
|
||||
pub shield_lifetime: Option<f32>,
|
||||
pub teleport_time: Option<f32>,
|
||||
pub camera_time: Option<f32>,
|
||||
pub camera_delay: Option<f32>,
|
||||
pub to_invisible_speed: Option<f32>,
|
||||
pub to_invisible_duration: Option<f32>,
|
||||
pub to_visible_duration: Option<f32>,
|
||||
pub countdown_time: Option<f32>,
|
||||
pub stun_time: Option<f32>,
|
||||
pub stun_radius: Option<f32>,
|
||||
pub effect_duration: Option<f32>,
|
||||
}
|
||||
|
||||
fn group_fire_scales_default() -> Vec<f32> {
|
||||
vec![1.0]
|
||||
}
|
||||
|
||||
impl std::convert::Into<crate::data::weapon_list::WeaponData> for WeaponData {
|
||||
fn into(self) -> crate::data::weapon_list::WeaponData {
|
||||
crate::data::weapon_list::WeaponData {
|
||||
damage_inflicted: self.damage_inflicted,
|
||||
protonium_damage_scale: self.protonium_damage_scale,
|
||||
projectile_speed: self.projectile_speed,
|
||||
projectile_range: self.projectile_range,
|
||||
base_inaccuracy: self.base_inaccuracy,
|
||||
base_air_inaccuracy: self.base_air_inaccuracy,
|
||||
movement_inaccuracy: self.movement_inaccuracy,
|
||||
movement_max_speed: self.movement_max_speed,
|
||||
movement_min_speed: self.movement_min_speed,
|
||||
gun_rotation_slow: self.gun_rotation_slow,
|
||||
movement_inaccuracy_decay: self.movement_inaccuracy_decay,
|
||||
slow_rotation_decay: self.slow_rotation_decay,
|
||||
quick_rotation_decay: self.quick_rotation_decay,
|
||||
movement_inaccuracy_recovery: self.movement_inaccuracy_recovery,
|
||||
repeat_fire_inaccuracy_total_degrees: self.repeat_fire_inaccuracy_total_degrees,
|
||||
repeat_fire_inaccuracy_decay: self.repeat_fire_inaccuracy_decay,
|
||||
repeat_fire_innaccuracy_recovery: self.repeat_fire_innaccuracy_recovery,
|
||||
fire_instant_accuracy_decay: self.fire_instant_accuracy_decay, // degrees
|
||||
accuracy_non_recover_time: self.accuracy_non_recover_time,
|
||||
accuracy_decay: self.accuracy_decay,
|
||||
damage_radius: self.damage_radius,
|
||||
plasma_time_to_full_damage: self.plasma_time_to_full_damage,
|
||||
plasma_starting_radius_scale: self.plasma_starting_radius_scale,
|
||||
nano_dps: self.nano_dps,
|
||||
nano_hps: self.nano_hps,
|
||||
tesla_damage: self.tesla_damage,
|
||||
tesla_charges: self.tesla_charges,
|
||||
aeroflak_proximity_damage: self.aeroflak_proximity_damage,
|
||||
aeroflak_damage_radius: self.aeroflak_damage_radius,
|
||||
aeroflak_explosion_radius: self.aeroflak_explosion_radius,
|
||||
aeroflak_ground_clearance: self.aeroflak_ground_clearance,
|
||||
aeroflak_max_stacks: self.aeroflak_max_stacks,
|
||||
aeroflak_damage_per_stack: self.aeroflak_damage_per_stack,
|
||||
aeroflak_stack_expire: self.aeroflak_stack_expire,
|
||||
shot_cooldown: self.shot_cooldown,
|
||||
smart_rotation_cooldown: self.smart_rotation_cooldown,
|
||||
smart_rotation_cooldown_extra: self.smart_rotation_cooldown_extra,
|
||||
smart_rotation_max_stacks: self.smart_rotation_max_stacks,
|
||||
spin_up_time: self.spin_up_time,
|
||||
spin_down_time: self.spin_down_time,
|
||||
spin_initial_cooldown: self.spin_initial_cooldown,
|
||||
group_fire_scales: self.group_fire_scales,
|
||||
mana_cost: self.mana_cost,
|
||||
lock_time: self.lock_time,
|
||||
full_lock_release: self.full_lock_release,
|
||||
change_lock_time: self.change_lock_time,
|
||||
max_rotation_speed: self.max_rotation_speed,
|
||||
initial_rotation_speed: self.initial_rotation_speed,
|
||||
rotation_acceleration: self.rotation_acceleration,
|
||||
nano_healing_priority_time: self.nano_healing_priority_time,
|
||||
module_range: self.module_range,
|
||||
shield_lifetime: self.shield_lifetime,
|
||||
teleport_time: self.teleport_time,
|
||||
camera_time: self.camera_time,
|
||||
camera_delay: self.camera_delay,
|
||||
to_invisible_speed: self.to_invisible_speed,
|
||||
to_invisible_duration: self.to_invisible_duration,
|
||||
to_visible_duration: self.to_visible_duration,
|
||||
countdown_time: self.countdown_time,
|
||||
stun_time: self.stun_time,
|
||||
stun_radius: self.stun_radius,
|
||||
effect_duration: self.effect_duration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct WeaponUpgradeInfo {
|
||||
pub xp: f64,
|
||||
pub rating: i32,
|
||||
pub rank: i32,
|
||||
pub power: i32,
|
||||
}
|
||||
|
||||
impl WeaponUpgradeInfo {
|
||||
pub fn into_data(self, tier: super::ItemTier, type_: super::ItemCategory) -> crate::data::weapon_upgrade::WeaponUpgradeInfo {
|
||||
crate::data::weapon_upgrade::WeaponUpgradeInfo {
|
||||
tier: tier.into(),
|
||||
type_: type_.into(),
|
||||
xp: self.xp,
|
||||
rating: self.rating,
|
||||
rank: self.rank,
|
||||
power: self.power,
|
||||
}
|
||||
}
|
||||
}
|
||||
71
rc_core/src/state.rs
Normal file
71
rc_core/src/state.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use crate::persist::user::UserProvider;
|
||||
use polariton_server::ToSend;
|
||||
|
||||
pub struct UserState<C: Clone = ()> {
|
||||
state: std::sync::RwLock<InitState<C>>,
|
||||
event_tx: tokio::sync::mpsc::UnboundedSender<ToSend<C>>,
|
||||
}
|
||||
|
||||
impl <C: Clone> UserState<C> {
|
||||
pub fn update_with_auth(&self, auth_str: &str) -> bool {
|
||||
let mut lock = self.state.write().unwrap();
|
||||
match &*lock {
|
||||
InitState::Unauthenticated(auth) => {
|
||||
let splits: Vec<&str> = auth_str.split(';').collect();
|
||||
if splits.len() != 3 {
|
||||
log::warn!("Invalid auth payload: {}", auth_str);
|
||||
false
|
||||
} else {
|
||||
let token = crate::persist::user::UserToken {
|
||||
uuid: splits[0].to_owned(),
|
||||
token: splits[1].to_owned(),
|
||||
refresh_token: splits[2].to_owned(),
|
||||
};
|
||||
match auth.authenticate(token) {
|
||||
Ok(user) => {
|
||||
*lock = InitState::Authenticated(std::sync::Arc::new(user));
|
||||
true
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Failed to authenticate {}: {}", splits[0], e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
InitState::Authenticated(_) => {
|
||||
log::warn!("User was already authenticated, ignoring");
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn new(provider: std::sync::Arc<crate::persist::user::UserImpl>, event_tx: tokio::sync::mpsc::UnboundedSender<ToSend<C>>) -> Self {
|
||||
Self {
|
||||
state: std::sync::RwLock::new(InitState::Unauthenticated(provider)),
|
||||
event_tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user(&self) -> Result<std::sync::Arc<Box<dyn crate::persist::user::User<C> + Send + Sync>>, i16> {
|
||||
let lock = self.state.read().unwrap();
|
||||
match &*lock {
|
||||
InitState::Unauthenticated(_) => Err(120),
|
||||
InitState::Authenticated(user) => Ok(user.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&self, event_data: ToSend<C>) {
|
||||
self.event_tx.send(event_data).unwrap()
|
||||
}
|
||||
|
||||
pub fn event_sender(&self) -> tokio::sync::mpsc::UnboundedSender<ToSend<C>> {
|
||||
self.event_tx.clone()
|
||||
}
|
||||
}
|
||||
|
||||
enum InitState<C> {
|
||||
Unauthenticated(std::sync::Arc<crate::persist::user::UserImpl>),
|
||||
Authenticated(std::sync::Arc<Box<dyn crate::persist::user::User<C> + Send + Sync>>),
|
||||
}
|
||||
Reference in New Issue
Block a user