diff --git a/src/load_roblox.rs b/src/load_roblox.rs index 0798cf0..ed8ef60 100644 --- a/src/load_roblox.rs +++ b/src/load_roblox.rs @@ -52,6 +52,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse force_can_collide=false; general.accelerator=Some(crate::model::GameMechanicAccelerator{acceleration:velocity}); }, + "UnorderedCheckpoint"=>general.checkpoint=Some(crate::model::GameMechanicCheckpoint::Unordered{mode_id:0}), "SetVelocity"=>general.trajectory=Some(crate::model::GameMechanicSetTrajectory::Velocity(velocity)), "MapFinish"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Finish})}, "MapAnticheat"=>{force_can_collide=false;general.zone=Some(crate::model::GameMechanicZone{mode_id:0,behaviour:crate::model::ZoneBehaviour::Anitcheat})}, @@ -110,6 +111,12 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,force_interse "WormholeIn"=>general.teleport_behaviour=Some(crate::model::TeleportBehaviour::Wormhole(crate::model::GameMechanicWormhole{destination_model_id:captures[2].parse::().unwrap()})), _=>panic!("regex3[1] messed up bad"), } + }else if let Some(captures)=lazy_regex::regex!(r"^(OrderedCheckpoint)(\d+)$") + .captures(other){ + match &captures[1]{ + "OrderedCheckpoint"=>general.checkpoint=Some(crate::model::GameMechanicCheckpoint::Ordered{mode_id:0,checkpoint_id:captures[2].parse::().unwrap()}), + _=>panic!("regex3[1] messed up bad"), + } } } } @@ -257,14 +264,12 @@ pub fn generate_indexed_models(dom:rbx_dom_weak::WeakDom) -> crate::model::Index spawn_point=model_transform.transform_point3(Planar64Vec3::ZERO)+Planar64Vec3::Y*5/2; Some(crate::model::TempIndexedAttributes::Start(crate::model::TempAttrStart{mode_id:0})) }, - "UnorderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::UnorderedCheckpoint(crate::model::TempAttrUnorderedCheckpoint{mode_id:0})), other=>{ - let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|OrderedCheckpoint|WormholeOut)(\d+)$"); + let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|WormholeOut)(\d+)$"); if let Some(captures) = regman.captures(other) { match &captures[1]{ "BonusStart"=>Some(crate::model::TempIndexedAttributes::Start(crate::model::TempAttrStart{mode_id:captures[2].parse::().unwrap()})), "Spawn"|"ForceSpawn"=>Some(crate::model::TempIndexedAttributes::Spawn(crate::model::TempAttrSpawn{mode_id:0,stage_id:captures[2].parse::().unwrap()})), - "OrderedCheckpoint"=>Some(crate::model::TempIndexedAttributes::OrderedCheckpoint(crate::model::TempAttrOrderedCheckpoint{mode_id:0,checkpoint_id:captures[2].parse::().unwrap()})), "WormholeOut"=>Some(crate::model::TempIndexedAttributes::Wormhole(crate::model::TempAttrWormhole{wormhole_id:captures[2].parse::().unwrap()})), _=>None, } diff --git a/src/model.rs b/src/model.rs index a7bd22a..c53e5ac 100644 --- a/src/model.rs +++ b/src/model.rs @@ -52,8 +52,6 @@ pub struct IndexedModelInstances{ pub struct ModeDescription{ pub start:usize,//start=model_id pub spawns:Vec,//spawns[spawn_id]=model_id - pub ordered_checkpoints:Vec,//ordered_checkpoints[checkpoint_id]=model_id - pub unordered_checkpoints:Vec,//unordered_checkpoints[checkpoint_id]=model_id pub spawn_from_stage_id:std::collections::HashMap::, pub ordered_checkpoint_from_checkpoint_id:std::collections::HashMap::, } @@ -61,9 +59,6 @@ impl ModeDescription{ pub fn get_spawn_model_id(&self,stage_id:u32)->Option<&usize>{ self.spawns.get(*self.spawn_from_stage_id.get(&stage_id)?) } - pub fn get_ordered_checkpoint_model_id(&self,checkpoint_id:u32)->Option<&usize>{ - self.ordered_checkpoints.get(*self.ordered_checkpoint_from_checkpoint_id.get(&checkpoint_id)?) - } } //I don't want this code to exist! #[derive(Clone)] @@ -76,23 +71,12 @@ pub struct TempAttrSpawn{ pub stage_id:u32, } #[derive(Clone)] -pub struct TempAttrOrderedCheckpoint{ - pub mode_id:u32, - pub checkpoint_id:u32, -} -#[derive(Clone)] -pub struct TempAttrUnorderedCheckpoint{ - pub mode_id:u32, -} -#[derive(Clone)] pub struct TempAttrWormhole{ pub wormhole_id:u32, } pub enum TempIndexedAttributes{ Start(TempAttrStart), Spawn(TempAttrSpawn), - OrderedCheckpoint(TempAttrOrderedCheckpoint), - UnorderedCheckpoint(TempAttrUnorderedCheckpoint), Wormhole(TempAttrWormhole), } @@ -126,6 +110,16 @@ pub enum GameMechanicBooster{ Energy{direction:Planar64Vec3,energy:Planar64},//increase energy in direction } #[derive(Clone)] +pub enum GameMechanicCheckpoint{ + Ordered{ + mode_id:u32, + checkpoint_id:u32, + }, + Unordered{ + mode_id:u32, + }, +} +#[derive(Clone)] pub enum TrajectoryChoice{ HighArcLongDuration,//underhand lob at target: less horizontal speed and more air time LowArcShortDuration,//overhand throw at target: more horizontal speed and less air time @@ -171,6 +165,13 @@ pub enum StageElementBehaviour{ Trigger, Teleport, Platform, + //Acts like a trigger if you haven't hit all the checkpoints. + Checkpoint{ + //if this is 2 you must have hit OrderedCheckpoint(0) OrderedCheckpoint(1) OrderedCheckpoint(2) to pass + ordered_checkpoint_id:Option, + //if this is 2 you must have hit at least 2 UnorderedCheckpoints to pass + unordered_checkpoint_count:u32, + }, JumpLimit(u32), //Speedtrap(TrapCondition),//Acts as a trigger with a speed condition } @@ -199,15 +200,17 @@ pub enum TeleportBehaviour{ pub struct GameMechanicAttributes{ pub zone:Option, pub booster:Option, + pub checkpoint:Option, pub trajectory:Option, pub teleport_behaviour:Option, pub accelerator:Option, } impl GameMechanicAttributes{ pub fn any(&self)->bool{ - self.booster.is_some() + self.zone.is_some() + ||self.booster.is_some() + ||self.checkpoint.is_some() ||self.trajectory.is_some() - ||self.zone.is_some() ||self.teleport_behaviour.is_some() ||self.accelerator.is_some() } diff --git a/src/physics.rs b/src/physics.rs index d0c2525..97fd93b 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -258,13 +258,18 @@ impl std::default::Default for PhysicsCamera{ } pub struct GameMechanicsState{ - pub stage_id:u32, - //jump_counts:HashMap, + stage_id:u32, + jump_counts:std::collections::HashMap,//model_id -> jump count + next_ordered_checkpoint_id:u32,//which OrderedCheckpoint model_id you must pass next (if 0 you haven't passed OrderedCheckpoint0) + unordered_checkpoints:std::collections::HashSet,//hashset of UnorderedCheckpoint model ids } impl std::default::Default for GameMechanicsState{ - fn default() -> Self { + fn default()->Self{ Self{ stage_id:0, + next_ordered_checkpoint_id:0, + unordered_checkpoints:std::collections::HashSet::new(), + jump_counts:std::collections::HashMap::new(), } } } @@ -784,8 +789,6 @@ impl PhysicsState { pub fn generate_models(&mut self,indexed_models:&crate::model::IndexedModelInstances){ let mut starts=Vec::new(); let mut spawns=Vec::new(); - let mut ordered_checkpoints=Vec::new(); - let mut unordered_checkpoints=Vec::new(); for model in &indexed_models.models{ //make aabb and run vertices to get realistic bounds for model_instance in &model.instances{ @@ -795,8 +798,6 @@ impl PhysicsState { match attr{ crate::model::TempIndexedAttributes::Start(s)=>starts.push((model_id,s.clone())), crate::model::TempIndexedAttributes::Spawn(s)=>spawns.push((model_id,s.clone())), - crate::model::TempIndexedAttributes::OrderedCheckpoint(s)=>ordered_checkpoints.push((model_id,s.clone())), - crate::model::TempIndexedAttributes::UnorderedCheckpoint(s)=>unordered_checkpoints.push((model_id,s.clone())), crate::model::TempIndexedAttributes::Wormhole(s)=>{self.models.model_id_from_wormhole_id.insert(s.wormhole_id,model_id);}, } } @@ -808,9 +809,9 @@ impl PhysicsState { //this code builds ModeDescriptions from the unsorted lists at the top of the function starts.sort_by_key(|tup|tup.1.mode_id); let mut mode_id_from_map_mode_id=std::collections::HashMap::new(); - let mut modedatas:Vec<(usize,Vec<(u32,usize)>,Vec<(u32,usize)>,Vec,u32)>=starts.into_iter().enumerate().map(|(i,(model_id,s))|{ + let mut modedatas:Vec<(usize,Vec<(u32,usize)>,u32)>=starts.into_iter().enumerate().map(|(i,(model_id,s))|{ mode_id_from_map_mode_id.insert(s.mode_id,i); - (model_id,Vec::new(),Vec::new(),Vec::new(),s.mode_id) + (model_id,Vec::new(),s.mode_id) }).collect(); for (model_id,s) in spawns{ if let Some(mode_id)=mode_id_from_map_mode_id.get(&s.mode_id){ @@ -819,30 +820,13 @@ impl PhysicsState { } } } - for (model_id,s) in ordered_checkpoints{ - if let Some(mode_id)=mode_id_from_map_mode_id.get(&s.mode_id){ - if let Some(modedata)=modedatas.get_mut(*mode_id){ - modedata.2.push((s.checkpoint_id,model_id)); - } - } - } - for (model_id,s) in unordered_checkpoints{ - if let Some(mode_id)=mode_id_from_map_mode_id.get(&s.mode_id){ - if let Some(modedata)=modedatas.get_mut(*mode_id){ - modedata.3.push(model_id); - } - } - } for mut tup in modedatas.into_iter(){ tup.1.sort_by_key(|tup|tup.0); - tup.2.sort_by_key(|tup|tup.0); let mut eshmep1=std::collections::HashMap::new(); let mut eshmep2=std::collections::HashMap::new(); - self.modes.insert(tup.4,crate::model::ModeDescription{ + self.modes.insert(tup.2,crate::model::ModeDescription{ start:tup.0, spawns:tup.1.into_iter().enumerate().map(|(i,tup)|{eshmep1.insert(tup.0,i);tup.1}).collect(), - ordered_checkpoints:tup.2.into_iter().enumerate().map(|(i,tup)|{eshmep2.insert(tup.0,i);tup.1}).collect(), - unordered_checkpoints:tup.3, spawn_from_stage_id:eshmep1, ordered_checkpoint_from_checkpoint_id:eshmep2, }); @@ -1273,6 +1257,11 @@ fn teleport(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,poi //TODO: calculate contacts and determine the actual state //touching.recalculate(body); } +fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,mode:&crate::model::ModeDescription,models:&PhysicsModels,stage_id:u32)->Option{ + let model=models.get(*mode.get_spawn_model_id(stage_id)? as usize)?; + let point=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox_halfsize.y()+Planar64::ONE/16); + Some(teleport(body,touching,style,point)) +} fn run_teleport_behaviour(teleport_behaviour:&Option,game:&mut GameMechanicsState,models:&PhysicsModels,modes:&Modes,style:&StyleModifiers,touching:&mut TouchingState,body:&mut Body,model:&ModelPhysics)->Option{ match teleport_behaviour{ @@ -1285,12 +1274,24 @@ fn run_teleport_behaviour(teleport_behaviour:&Option{ //I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird - let model=models.get(*modes.get_mode(stage_element.mode_id)?.get_spawn_model_id(game.stage_id)? as usize)?; - let point=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox_halfsize.y()+Planar64::ONE/16); - Some(teleport(body,touching,style,point)) + teleport_to_spawn(body,touching,style,modes.get_mode(stage_element.mode_id)?,models,game.stage_id) }, crate::model::StageElementBehaviour::Platform=>None, - crate::model::StageElementBehaviour::JumpLimit(_)=>None,//TODO + &crate::model::StageElementBehaviour::JumpLimit(jump_limit)=>{ + //let count=game.jump_counts.get(&model.id); + //TODO + None + }, + &crate::model::StageElementBehaviour::Checkpoint{ordered_checkpoint_id,unordered_checkpoint_count}=>{ + if (ordered_checkpoint_id.is_none()||ordered_checkpoint_id.is_some_and(|id|id{