diff --git a/lib/common/src/gameplay_modes.rs b/lib/common/src/gameplay_modes.rs index d0d204bde..b9bdb24c5 100644 --- a/lib/common/src/gameplay_modes.rs +++ b/lib/common/src/gameplay_modes.rs @@ -128,18 +128,6 @@ impl Stage{ self.unordered_checkpoints.contains(&model_id) } } -#[derive(Default)] -pub struct StageUpdate{ - //other behaviour models of this stage can have - ordered_checkpoints:HashMap<CheckpointId,ModelId>, - unordered_checkpoints:HashSet<ModelId>, -} -impl Updatable<StageUpdate> for Stage{ - fn update(&mut self,update:StageUpdate){ - self.ordered_checkpoints.extend(update.ordered_checkpoints); - self.unordered_checkpoints.extend(update.unordered_checkpoints); - } -} #[derive(Clone,Copy,Hash,Eq,PartialEq)] pub enum Zone{ @@ -256,47 +244,6 @@ impl Mode{ } } } -//this would be nice as a macro -#[derive(Default)] -pub struct ModeUpdate{ - zones:HashMap<ModelId,Zone>, - stages:HashMap<StageId,StageUpdate>, - //mutually exlusive stage element behaviour - elements:HashMap<ModelId,StageElement>, -} -impl Updatable<ModeUpdate> for Mode{ - fn update(&mut self,update:ModeUpdate){ - self.zones.extend(update.zones); - for (stage,stage_update) in update.stages{ - if let Some(stage)=self.stages.get_mut(stage.0 as usize){ - stage.update(stage_update); - } - } - self.elements.extend(update.elements); - } -} -impl ModeUpdate{ - pub fn zone(model_id:ModelId,zone:Zone)->Self{ - let mut mu=Self::default(); - mu.zones.insert(model_id,zone); - mu - } - pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{ - let mut mu=Self::default(); - mu.stages.insert(stage_id,stage_update); - mu - } - pub fn element(model_id:ModelId,element:StageElement)->Self{ - let mut mu=Self::default(); - mu.elements.insert(model_id,element); - mu - } - pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){ - for (_,stage_element) in self.elements.iter_mut(){ - stage_element.stage_id=f(stage_element.stage_id); - } - } -} #[derive(Default,Clone)] pub struct Modes{ @@ -318,15 +265,165 @@ impl Modes{ self.modes.get(mode.0 as usize) } } + + +#[derive(Default)] +pub struct StageUpdate{ + //other behaviour models of this stage can have + ordered_checkpoints:HashMap<CheckpointId,ModelId>, + unordered_checkpoints:HashSet<ModelId>, +} +impl Updatable<StageUpdate> for Stage{ + fn update(&mut self,update:StageUpdate){ + self.ordered_checkpoints.extend(update.ordered_checkpoints); + self.unordered_checkpoints.extend(update.unordered_checkpoints); + } +} + +//this would be nice as a macro +#[derive(Default)] +pub struct ModeUpdate{ + zones:Option<(ModelId,Zone)>, + stages:Option<(StageId,StageUpdate)>, + //mutually exlusive stage element behaviour + elements:Option<(ModelId,StageElement)>, +} +impl Updatable<ModeUpdate> for Mode{ + fn update(&mut self,update:ModeUpdate){ + self.zones.extend(update.zones); + if let Some((StageId(stage_id),stage_update))=update.stages{ + if let Some(stage)=self.stages.get_mut(stage_id as usize){ + stage.update(stage_update); + } + } + self.elements.extend(update.elements); + } +} +impl ModeUpdate{ + pub fn zone(model_id:ModelId,zone:Zone)->Self{ + Self{ + zones:Some((model_id,zone)), + stages:None, + elements:None, + } + } + pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{ + Self{ + zones:None, + stages:Some((stage_id,stage_update)), + elements:None, + } + } + pub fn element(model_id:ModelId,element:StageElement)->Self{ + Self{ + zones:None, + stages:None, + elements:Some((model_id,element)), + } + } + pub fn map_stage_element_ids<F:Fn(StageId)->StageId>(&mut self,f:F){ + for (_,stage_element) in self.elements.iter_mut(){ + stage_element.stage_id=f(stage_element.stage_id); + } + } +} pub struct ModesUpdate{ modes:HashMap<ModeId,ModeUpdate>, } impl Updatable<ModesUpdate> for Modes{ fn update(&mut self,update:ModesUpdate){ - for (mode,mode_update) in update.modes{ - if let Some(mode)=self.modes.get_mut(mode.0 as usize){ + for (ModeId(mode_id),mode_update) in update.modes{ + if let Some(mode)=self.modes.get_mut(mode_id as usize){ mode.update(mode_update); } } } } + +struct ModeBuilder{ + mode:Mode, + final_stage_id_from_builder_stage_id:HashMap<StageId,StageId>, +} +#[derive(Default)] +pub struct ModesBuilder{ + modes:HashMap<ModeId,Mode>, + stages:HashMap<ModeId,HashMap<StageId,Stage>>, + mode_updates:Vec<(ModeId,ModeUpdate)>, + stage_updates:Vec<(ModeId,StageId,StageUpdate)>, +} +impl ModesBuilder{ + pub fn build(mut self)->Modes{ + //collect modes and stages into contiguous arrays + let mut unique_modes:Vec<(ModeId,Mode)> + =self.modes.into_iter().collect(); + unique_modes.sort_by_key(|&(mode_id,_)|mode_id); + let (mut modes,final_mode_id_from_builder_mode_id):(Vec<ModeBuilder>,HashMap<ModeId,ModeId>) + =unique_modes.into_iter().enumerate() + .map(|(final_mode_id,(builder_mode_id,mut mode))|{ + ( + ModeBuilder{ + final_stage_id_from_builder_stage_id:self.stages.remove(&builder_mode_id).map_or_else(||HashMap::new(),|stages|{ + let mut unique_stages:Vec<(StageId,Stage)> + =stages.into_iter().collect(); + unique_stages.sort_by(|a,b|a.0.cmp(&b.0)); + unique_stages.into_iter().enumerate() + .map(|(final_stage_id,(builder_stage_id,stage))|{ + mode.push_stage(stage); + (builder_stage_id,StageId::new(final_stage_id as u32)) + }).collect() + }), + mode, + }, + ( + builder_mode_id, + ModeId::new(final_mode_id as u32) + ) + ) + }).unzip(); + //TODO: failure messages or errors or something + //push stage updates + for (builder_mode_id,builder_stage_id,stage_update) in self.stage_updates{ + if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){ + if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){ + if let Some(&final_stage_id)=mode.final_stage_id_from_builder_stage_id.get(&builder_stage_id){ + if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){ + stage.update(stage_update); + } + } + } + } + } + //push mode updates + for (builder_mode_id,mut mode_update) in self.mode_updates{ + if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){ + if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){ + //map stage id on stage elements + mode_update.map_stage_element_ids(|stage_id| + //walk down one stage id at a time until a stage is found + //TODO use better logic like BTreeMap::upper_bound instead of walking + // final_stage_id_from_builder_stage_id.upper_bound(Bound::Included(&stage_id)) + // .value().copied().unwrap_or(StageId::FIRST) + (0..=stage_id.get()).rev().find_map(|builder_stage_id| + //map the stage element to that stage + mode.final_stage_id_from_builder_stage_id.get(&StageId::new(builder_stage_id)).copied() + ).unwrap_or(StageId::FIRST) + ); + mode.mode.update(mode_update); + } + } + } + Modes::new(modes.into_iter().map(|mode_builder|mode_builder.mode).collect()) + } + pub fn insert_mode(&mut self,mode_id:ModeId,mode:Mode){ + assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode"); + } + pub fn insert_stage(&mut self,mode_id:ModeId,stage_id:StageId,stage:Stage){ + assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage"); + } + pub fn push_mode_update(&mut self,mode_id:ModeId,mode_update:ModeUpdate){ + self.mode_updates.push((mode_id,mode_update)); + } + // fn push_stage_update(&mut self,mode_id:ModeId,stage_id:StageId,stage_update:StageUpdate){ + // self.stage_updates.push((mode_id,stage_id,stage_update)); + // } +} diff --git a/lib/rbx_loader/src/rbx.rs b/lib/rbx_loader/src/rbx.rs index 8162662aa..208394d0e 100644 --- a/lib/rbx_loader/src/rbx.rs +++ b/lib/rbx_loader/src/rbx.rs @@ -4,12 +4,11 @@ use crate::primitives; use strafesnet_common::aabb::Aabb; use strafesnet_common::map; use strafesnet_common::model; -use strafesnet_common::gameplay_modes; +use strafesnet_common::gameplay_modes::{Mode,ModeId,ModeUpdate,Modes,ModesBuilder,Stage,StageElement,StageElementBehaviour,StageId,Zone}; use strafesnet_common::gameplay_style; use strafesnet_common::gameplay_attributes as attr; use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3}; use strafesnet_common::model::RenderConfigId; -use strafesnet_common::updatable::Updatable; use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,MeshDeferredLoader}; use strafesnet_deferred_loader::mesh::Meshes; use strafesnet_deferred_loader::texture::{RenderConfigs,Texture}; @@ -52,93 +51,7 @@ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_we vec3::try_from_f32_array([cf.position.x,cf.position.y,cf.position.z]).unwrap() ) } -struct ModeBuilder{ - mode:gameplay_modes::Mode, - final_stage_id_from_builder_stage_id:HashMap<gameplay_modes::StageId,gameplay_modes::StageId>, -} -#[derive(Default)] -struct ModesBuilder{ - modes:HashMap<gameplay_modes::ModeId,gameplay_modes::Mode>, - stages:HashMap<gameplay_modes::ModeId,HashMap<gameplay_modes::StageId,gameplay_modes::Stage>>, - mode_updates:Vec<(gameplay_modes::ModeId,gameplay_modes::ModeUpdate)>, - stage_updates:Vec<(gameplay_modes::ModeId,gameplay_modes::StageId,gameplay_modes::StageUpdate)>, -} -impl ModesBuilder{ - fn build(mut self)->gameplay_modes::Modes{ - //collect modes and stages into contiguous arrays - let mut unique_modes:Vec<(gameplay_modes::ModeId,gameplay_modes::Mode)> - =self.modes.into_iter().collect(); - unique_modes.sort_by_key(|&(mode_id,_)|mode_id); - let (mut modes,final_mode_id_from_builder_mode_id):(Vec<ModeBuilder>,HashMap<gameplay_modes::ModeId,gameplay_modes::ModeId>) - =unique_modes.into_iter().enumerate() - .map(|(final_mode_id,(builder_mode_id,mut mode))|{ - ( - ModeBuilder{ - final_stage_id_from_builder_stage_id:self.stages.remove(&builder_mode_id).map_or_else(||HashMap::new(),|stages|{ - let mut unique_stages:Vec<(gameplay_modes::StageId,gameplay_modes::Stage)> - =stages.into_iter().collect(); - unique_stages.sort_by(|a,b|a.0.cmp(&b.0)); - unique_stages.into_iter().enumerate() - .map(|(final_stage_id,(builder_stage_id,stage))|{ - mode.push_stage(stage); - (builder_stage_id,gameplay_modes::StageId::new(final_stage_id as u32)) - }).collect() - }), - mode, - }, - ( - builder_mode_id, - gameplay_modes::ModeId::new(final_mode_id as u32) - ) - ) - }).unzip(); - //TODO: failure messages or errors or something - //push stage updates - for (builder_mode_id,builder_stage_id,stage_update) in self.stage_updates{ - if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){ - if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){ - if let Some(&final_stage_id)=mode.final_stage_id_from_builder_stage_id.get(&builder_stage_id){ - if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){ - stage.update(stage_update); - } - } - } - } - } - //push mode updates - for (builder_mode_id,mut mode_update) in self.mode_updates{ - if let Some(final_mode_id)=final_mode_id_from_builder_mode_id.get(&builder_mode_id){ - if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){ - //map stage id on stage elements - mode_update.map_stage_element_ids(|stage_id| - //walk down one stage id at a time until a stage is found - //TODO use better logic like BTreeMap::upper_bound instead of walking - // final_stage_id_from_builder_stage_id.upper_bound(Bound::Included(&stage_id)) - // .value().copied().unwrap_or(gameplay_modes::StageId::FIRST) - (0..=stage_id.get()).rev().find_map(|builder_stage_id| - //map the stage element to that stage - mode.final_stage_id_from_builder_stage_id.get(&gameplay_modes::StageId::new(builder_stage_id)).copied() - ).unwrap_or(gameplay_modes::StageId::FIRST) - ); - mode.mode.update(mode_update); - } - } - } - gameplay_modes::Modes::new(modes.into_iter().map(|mode_builder|mode_builder.mode).collect()) - } - fn insert_mode(&mut self,mode_id:gameplay_modes::ModeId,mode:gameplay_modes::Mode){ - assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode"); - } - fn insert_stage(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage:gameplay_modes::Stage){ - assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage"); - } - fn push_mode_update(&mut self,mode_id:gameplay_modes::ModeId,mode_update:gameplay_modes::ModeUpdate){ - self.mode_updates.push((mode_id,mode_update)); - } - // fn push_stage_update(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage_update:gameplay_modes::StageUpdate){ - // self.stage_updates.push((mode_id,stage_id,stage_update)); - // } -} + fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>,wormhole_id_to_out_model:&mut HashMap<u32,model::ModelId>)->attr::CollisionAttributes{ let mut general=attr::GeneralAttributes::default(); let mut intersecting=attr::IntersectingAttributes::default(); @@ -167,8 +80,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode force_can_collide=false; force_intersecting=true; modes_builder.insert_mode( - gameplay_modes::ModeId::MAIN, - gameplay_modes::Mode::empty( + ModeId::MAIN, + Mode::empty( gameplay_style::StyleModifiers::roblox_bhop(), model_id ) @@ -178,10 +91,10 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode force_can_collide=false; force_intersecting=true; modes_builder.push_mode_update( - gameplay_modes::ModeId::MAIN, - gameplay_modes::ModeUpdate::zone( + ModeId::MAIN, + ModeUpdate::zone( model_id, - gameplay_modes::Zone::Finish, + Zone::Finish, ), ); }, @@ -189,19 +102,19 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode force_can_collide=false; force_intersecting=true; modes_builder.push_mode_update( - gameplay_modes::ModeId::MAIN, - gameplay_modes::ModeUpdate::zone( + ModeId::MAIN, + ModeUpdate::zone( model_id, - gameplay_modes::Zone::Anticheat, + Zone::Anticheat, ), ); }, "Platform"=>{ modes_builder.push_mode_update( - gameplay_modes::ModeId::MAIN, - gameplay_modes::ModeUpdate::element( + ModeId::MAIN, + ModeUpdate::element( model_id, - gameplay_modes::StageElement::new(gameplay_modes::StageId::FIRST,false,gameplay_modes::StageElementBehaviour::Platform,None),//roblox does not know which stage the platform belongs to + StageElement::new(StageId::FIRST,false,StageElementBehaviour::Platform,None),//roblox does not know which stage the platform belongs to ), ); }, @@ -213,8 +126,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode force_can_collide=false; force_intersecting=true; modes_builder.insert_mode( - gameplay_modes::ModeId::new(captures[2].parse::<u32>().unwrap()), - gameplay_modes::Mode::empty( + ModeId::new(captures[2].parse::<u32>().unwrap()), + Mode::empty( gameplay_style::StyleModifiers::roblox_bhop(), model_id ) @@ -231,8 +144,8 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode }else if let Some(captures)=lazy_regex::regex!(r"^(Force)?(Spawn|SpawnAt|Trigger|Teleport|Platform)(\d+)$") .captures(other){ force_intersecting=true; - let stage_id=gameplay_modes::StageId::new(captures[3].parse::<u32>().unwrap()); - let stage_element=gameplay_modes::StageElement::new( + let stage_id=StageId::new(captures[3].parse::<u32>().unwrap()); + let stage_element=StageElement::new( //stage_id: stage_id, //force: @@ -244,26 +157,26 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode match &captures[2]{ "Spawn"=>{ modes_builder.insert_stage( - gameplay_modes::ModeId::MAIN, + ModeId::MAIN, stage_id, - gameplay_modes::Stage::empty(model_id), + Stage::empty(model_id), ); //TODO: let denormalize handle this - gameplay_modes::StageElementBehaviour::SpawnAt + StageElementBehaviour::SpawnAt }, - "SpawnAt"=>gameplay_modes::StageElementBehaviour::SpawnAt, + "SpawnAt"=>StageElementBehaviour::SpawnAt, //cancollide false so you don't hit the side //NOT a decoration - "Trigger"=>{force_can_collide=false;gameplay_modes::StageElementBehaviour::Trigger}, - "Teleport"=>{force_can_collide=false;gameplay_modes::StageElementBehaviour::Teleport}, - "Platform"=>gameplay_modes::StageElementBehaviour::Platform, + "Trigger"=>{force_can_collide=false;StageElementBehaviour::Trigger}, + "Teleport"=>{force_can_collide=false;StageElementBehaviour::Teleport}, + "Platform"=>StageElementBehaviour::Platform, _=>panic!("regex1[2] messed up bad"), }, None ); modes_builder.push_mode_update( - gameplay_modes::ModeId::MAIN, - gameplay_modes::ModeUpdate::element( + ModeId::MAIN, + ModeUpdate::element( model_id, stage_element, ), @@ -272,14 +185,14 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode .captures(other){ match &captures[1]{ "Jump"=>modes_builder.push_mode_update( - gameplay_modes::ModeId::MAIN, - gameplay_modes::ModeUpdate::element( + ModeId::MAIN, + ModeUpdate::element( model_id, //jump_limit: - gameplay_modes::StageElement::new( - gameplay_modes::StageId::FIRST, + StageElement::new( + StageId::FIRST, false, - gameplay_modes::StageElementBehaviour::Check, + StageElementBehaviour::Check, Some(captures[2].parse::<u8>().unwrap()) ) ), @@ -296,13 +209,13 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode force_can_collide=false; force_intersecting=true; modes_builder.push_mode_update( - gameplay_modes::ModeId::new(captures[2].parse::<u32>().unwrap()), - gameplay_modes::ModeUpdate::zone( + ModeId::new(captures[2].parse::<u32>().unwrap()), + ModeUpdate::zone( model_id, //zone: match &captures[1]{ - "Finish"=>gameplay_modes::Zone::Finish, - "Anticheat"=>gameplay_modes::Zone::Anticheat, + "Finish"=>Zone::Finish, + "Anticheat"=>Zone::Anticheat, _=>panic!("regex3[1] messed up bad"), }, ), @@ -312,9 +225,9 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode // .captures(other){ // match &captures[1]{ // "OrderedCheckpoint"=>modes_builder.push_stage_update( - // gameplay_modes::ModeId::MAIN, - // gameplay_modes::StageId::new(0), - // gameplay_modes::StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()), + // ModeId::MAIN, + // StageId::new(0), + // StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()), // ), // _=>panic!("regex3[1] messed up bad"), // } @@ -1018,7 +931,7 @@ impl PartialMap1<'_>{ pub struct PartialMap2{ meshes:Vec<model::Mesh>, models:Vec<model::Model>, - modes:gameplay_modes::Modes, + modes:Modes, attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>, } impl PartialMap2{