This commit is contained in:
Quaternions 2024-02-01 19:43:02 -08:00
parent 56859d4e0c
commit 2cc7d76a48

View File

@ -3,8 +3,10 @@ use crate::primitives;
use strafesnet_common::map;
use strafesnet_common::model;
use strafesnet_common::gameplay_modes;
use strafesnet_common::gameplay_style;
use strafesnet_common::gameplay_attributes as attr;
use strafesnet_common::integer::{Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
use strafesnet_common::updatable::Updatable;
fn class_is_a(class: &str, superclass: &str) -> bool {
if class==superclass {
@ -44,20 +46,84 @@ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_we
Planar64Vec3::try_from([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(|a,b|a.0.cmp(&b.0));
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::id(final_stage_id as u32))
}).collect()
}),
mode,
},
(
builder_mode_id,
gameplay_modes::ModeId::id(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,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){
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(modes_builder:&mut ModesBuilder,model_id:model::ModelId,name:&str,can_collide:bool,velocity:Planar64Vec3,force_intersecting:bool)->attr::CollisionAttributes{
fn get_attributes(object:&rbx_dom_weak::Instance,can_collide:bool,velocity:Planar64Vec3,force_intersecting:bool,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>)->attr::CollisionAttributes{
let name=object.name.as_str();
let mut general=attr::GeneralAttributes::default();
let mut intersecting=attr::IntersectingAttributes::default();
let mut contacting=attr::ContactingAttributes::default();
@ -137,21 +203,27 @@ fn get_attributes(modes_builder:&mut ModesBuilder,model_id:model::ModelId,name:&
),
),
);
}else if let Some(captures)=lazy_regex::regex!(r"^Jump(\d+)$")
}else if let Some(captures)=lazy_regex::regex!(r"^(Jump|WormholeIn)(\d+)$")
.captures(other){
modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN,
gameplay_modes::ModeUpdate::jump_limit(
model_id,
//jump_limit:
captures[1].parse::<u32>().unwrap()
match &captures[1]{
"Jump"=>modes_builder.push_mode_update(
gameplay_modes::ModeId::MAIN,
gameplay_modes::ModeUpdate::jump_limit(
model_id,
//jump_limit:
captures[2].parse::<u32>().unwrap()
),
),
);
"WormholeIn"=>{
force_can_collide=false;
assert!(wormhole_in_model_to_id.insert(model_id,captures[2].parse::<u32>().unwrap()).is_none(),"Impossible");
},
}
}else if let Some(captures)=lazy_regex::regex!(r"^Bonus(Finish|Anticheat)(\d+)$")
.captures(other){
force_can_collide=false;
modes_builder.push_mode_update(
gameplay_modes::ModeId::mode(captures[2].parse::<u32>().unwrap()),
gameplay_modes::ModeId::id(captures[2].parse::<u32>().unwrap()),
gameplay_modes::ModeUpdate::zone(
model_id,
//zone:
@ -162,15 +234,15 @@ fn get_attributes(modes_builder:&mut ModesBuilder,model_id:model::ModelId,name:&
},
),
);
}else if let Some(captures)=lazy_regex::regex!(r"^WormholeIn(\d+)$")
.captures(other){
force_can_collide=false;
general.wormhole=Some(attr::Wormhole{destination_model_id:captures[1].parse::<u32>().unwrap()});
}
// else if let Some(captures)=lazy_regex::regex!(r"^(OrderedCheckpoint)(\d+)$")
// else if let Some(captures)=lazy_regex::regex!(r"^Stage(\d+)OrderedCheckpoint(\d+)$")
// .captures(other){
// match &captures[1]{
// "OrderedCheckpoint"=>general.checkpoint=Some(attr::Checkpoint::Ordered{mode_id:0,checkpoint_id:captures[2].parse::<u32>().unwrap()}),
// "OrderedCheckpoint"=>modes_builder.push_stage_update(
// gameplay_modes::ModeId::MAIN,
// gameplay_modes::StageId::id(0),
// gameplay_modes::StageUpdate::ordered_checkpoint(captures[2].parse::<u32>().unwrap()),
// ),
// _=>panic!("regex3[1] messed up bad"),
// }
// }
@ -279,12 +351,24 @@ enum RobloxBasePartDescription{
Wedge(RobloxWedgeDescription),
CornerWedge(RobloxCornerWedgeDescription),
}
struct ModelOwnedAttributes{
model:model::IndexedModelId,
attributes:attr::CollisionAttributes,
color:model::Color4,//transparency is in here
transform:Planar64Affine3,
}
pub fn convert(dom:rbx_dom_weak::WeakDom)->map::Map{
//IndexedModelInstances includes textures
let mut spawn_point=Planar64Vec3::ZERO;
let mut modes_builder=ModesBuilder::default();
let mut models1=Vec::new();
let mut indexed_models=Vec::new();
let mut model_id_from_description=std::collections::HashMap::<RobloxBasePartDescription,usize>::new();
let mut indexed_model_id_from_description=std::collections::HashMap::new();
let mut unique_attributes=Vec::new();
let mut attributes_id_from_attributes=HashMap::new();
let mut wormhole_in_model_to_id=HashMap::new();
let mut wormhole_id_to_out_model=HashMap::new();
let mut texture_id_from_asset_id=std::collections::HashMap::<u64,u32>::new();
let mut asset_id_from_texture_id=Vec::new();
@ -324,31 +408,54 @@ pub fn convert(dom:rbx_dom_weak::WeakDom)->map::Map{
continue;
}
//push TempIndexedAttributes
let mut force_intersecting=false;
let mut temp_indexing_attributes=Vec::new();
if let Some(attr)=match &object.name[..]{
//at this point a new model is going to be generated for sure.
let model_id=model::ModelId::id(models1.len() as u32);
let force_intersecting=match &object.name[..]{
"MapStart"=>{
spawn_point=model_transform.transform_point3(Planar64Vec3::ZERO)+Planar64Vec3::Y*5/2;
Some(model::TempIndexedAttributes::Start(model::TempAttrStart{mode_id:0}))
modes_builder.insert_mode(
gameplay_modes::ModeId::MAIN,
gameplay_modes::Mode::new(
gameplay_style::StyleModifiers::roblox_bhop(),
model_id
)
);
true
},
other=>{
let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|ForceSpawn|WormholeOut)(\d+)$");
if let Some(captures) = regman.captures(other) {
let regman=lazy_regex::regex!(r"^(BonusStart|Spawn|WormholeOut)(\d+)$");
if let Some(captures)=regman.captures(other){
match &captures[1]{
"BonusStart"=>Some(model::TempIndexedAttributes::Start(model::TempAttrStart{mode_id:captures[2].parse::<u32>().unwrap()})),
"Spawn"|"ForceSpawn"=>Some(model::TempIndexedAttributes::Spawn(model::TempAttrSpawn{mode_id:0,stage_id:captures[2].parse::<u32>().unwrap()})),
"WormholeOut"=>Some(model::TempIndexedAttributes::Wormhole(model::TempAttrWormhole{wormhole_id:captures[2].parse::<u32>().unwrap()})),
_=>None,
"BonusStart"=>{
modes_builder.insert_mode(
gameplay_modes::ModeId::id(captures[2].parse::<u32>().unwrap()),
gameplay_modes::Mode::new(
gameplay_style::StyleModifiers::roblox_bhop(),
model_id
)
);
true
},
"Spawn"=>{
modes_builder.insert_stage(
gameplay_modes::ModeId::MAIN,
gameplay_modes::StageId::id(captures[2].parse::<u32>().unwrap()),
gameplay_modes::Stage::new(model_id),
);
true
},
"WormholeOut"=>{
//this object is not special in strafe client, but the roblox mapping needs to be converted to model id
assert!(wormhole_id_to_out_model.insert(captures[2].parse::<u32>().unwrap(),model_id).is_none(),"Cannot have multiple WormholeOut with same id");
false
},
_=>false,
}
}else{
None
false
}
}
}{
force_intersecting=true;
temp_indexing_attributes.push(attr);
}
};
//TODO: also detect "CylinderMesh" etc here
let shape=match &object.class[..]{
@ -482,12 +589,12 @@ pub fn convert(dom:rbx_dom_weak::WeakDom)->map::Map{
]),
};
//make new model if unit cube has not been created before
let model_id=if let Some(&model_id)=model_id_from_description.get(&basepart_texture_description){
let indexed_model_id=if let Some(&indexed_model_id)=indexed_model_id_from_description.get(&basepart_texture_description){
//push to existing texture model
model_id
indexed_model_id
}else{
let model_id=indexed_models.len();
model_id_from_description.insert(basepart_texture_description.clone(),model_id);//borrow checker going crazy
let indexed_model_id=model::IndexedModelId::id(indexed_models.len() as u32);
indexed_model_id_from_description.insert(basepart_texture_description.clone(),indexed_model_id);//borrow checker going crazy
indexed_models.push(match basepart_texture_description{
RobloxBasePartDescription::Sphere(part_texture_description)
|RobloxBasePartDescription::Cylinder(part_texture_description)
@ -550,21 +657,67 @@ pub fn convert(dom:rbx_dom_weak::WeakDom)->map::Map{
primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description)
},
});
model_id
indexed_model_id
};
indexed_models[model_id].instances.push(model::Model{
let attributes=get_attributes(
&object,
*can_collide,
Planar64Vec3::try_from([velocity.x,velocity.y,velocity.z]).unwrap(),
force_intersecting,
model_id,
&mut modes_builder,
&mut wormhole_in_model_to_id,
);
models1.push(ModelOwnedAttributes{
model:indexed_model_id,
transform:model_transform,
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
attributes:get_attributes(&object.name,*can_collide,Planar64Vec3::try_from([velocity.x,velocity.y,velocity.z]).unwrap(),force_intersecting),
temp_indexing:temp_indexing_attributes,
attributes,
});
}
}
}
model::IndexedModelInstances{
textures:asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),
models:indexed_models,
spawn_point,
modes:Vec::new(),
let models=models1.into_iter().enumerate().map(|(model_id,mut model1)|{
let model_id=model::ModelId::id(model_id as u32);
//update attributes with wormhole id
//TODO: errors/prints
if let Some(wormhole_id)=wormhole_in_model_to_id.get(&model_id){
if let Some(&wormhole_out_model_id)=wormhole_id_to_out_model.get(wormhole_id){
match &mut model1.attributes{
attr::CollisionAttributes::Contact{contacting:_,general}
|attr::CollisionAttributes::Intersect{intersecting:_,general}
=>general.wormhole=Some(attr::Wormhole{destination_model:wormhole_out_model_id}),
attr::CollisionAttributes::Decoration=>println!("Not a wormhole"),
}
}
}
//index the attributes
let attributes_id=if let Some(&attributes_id)=attributes_id_from_attributes.get(&model1.attributes){
attributes_id
}else{
let attributes_id=attr::CollisionAttributesId::new(unique_attributes.len() as u32);
attributes_id_from_attributes.insert(model1.attributes,attributes_id);
unique_attributes.push(model1.attributes);
attributes_id
};
(model_id,model::Model{
model:model1.model,
transform:model1.transform,
color:model1.color,
attributes:attributes_id,
})
}).collect();
map::Map{
textures:Vec::new(),//asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),
models:model::Models::new(
indexed_models.into_iter().enumerate()
.map(|(indexed_model_id,indexed_model)|
(model::IndexedModelId::id(indexed_model_id as u32),indexed_model)
).collect(),
models,
),
modes:modes_builder.build(),
attributes:unique_attributes,
}
}