Compare commits

..

35 Commits

Author SHA1 Message Date
a56195c887 fix compile 2025-03-11 17:50:06 -07:00
70fb970582 bsp_loader: create a cube of destiny for teleport destinations 2025-03-11 17:28:53 -07:00
31184d21da bsp_loader: transform spawn after loop 2025-03-11 17:28:53 -07:00
6b4b17f49d bsp_loader: use macros for entities 2025-03-11 17:28:53 -07:00
d0c17873f6 bsp_loader: no physics for illusionary 2025-03-11 17:28:53 -07:00
d39705d5e7 bsp_loader: document known entities 2025-03-11 17:28:53 -07:00
a3a50580f8 bsp_loader: define all entities 2025-03-11 17:28:53 -07:00
d5bf4549a9 bsp_loader: triggers are intersect 2025-03-11 17:28:53 -07:00
b5da85fae1 bsp_loader: const ENTITY_ATTRIBUTE 2025-03-11 17:28:53 -07:00
78c53ae429 physics: set style on reset and spawn 2025-03-11 17:09:29 -07:00
e794b2649c bsp_loader: implement ladders and water 2025-03-11 16:41:46 -07:00
50f6fe5bd8 common: fix source styles 2025-03-11 15:26:45 -07:00
fc3681f41f it: read_entire_file use built in 2025-03-11 14:07:24 -07:00
a2369b4211 rbx_loader: never do &Vec + deconstruct Face2 (style) 2025-03-09 23:47:23 -07:00
bfea49ffae it: measure simulate speed 2025-03-08 17:39:47 -08:00
8c7063d340 common: aabb: make function inline-able 2025-03-08 14:34:54 -08:00
651150760c common: aabb: use midpoint for center 2025-03-08 14:34:54 -08:00
3d203e2da9 fixed_wide: add midpoint 2025-03-08 14:34:54 -08:00
3798f438cf use as_bits_mut to optimize sqrt 2025-03-08 13:58:56 -08:00
4ace8317fe update deps 2025-03-08 13:55:14 -08:00
ab4a9bb922 bsp_loader: generate prop matrix with euler angles 2025-03-02 18:23:47 -08:00
a9ef07ce78 update vbsp 2025-03-02 18:23:47 -08:00
561e41c760 common: delete unused ModesUpdate 2025-02-28 13:15:55 -08:00
9896a09576 common: delete updatable trait 2025-02-28 13:15:55 -08:00
82840fa6cb refactor type safety for modes data normalization 2025-02-28 13:15:55 -08:00
2c87cb71df common: gameplay_modes: rename internal field on ModeBuilder 2025-02-28 12:37:49 -08:00
709f622547 common: don't use .0 for newtypes 2025-02-28 12:10:16 -08:00
b20264b382 common: move ModesBuilder to common 2025-02-28 11:08:38 -08:00
17cce2d702 silence lints 2025-02-28 10:50:53 -08:00
a2b2ddc9ce bsp_loader: use texture_info function 2025-02-26 15:29:23 -08:00
cb57d893e0 update vbsp fork 2025-02-26 15:26:56 -08:00
9e8d66cec1 map-tool: gimme_them_textures iterate over a shorter list of prop model paths 2025-02-26 12:00:51 -08:00
39fe833198 map-tool: grab brush models 2025-02-26 12:00:51 -08:00
e7688a95fd bsp_loader: generate physics from brushes 2025-02-26 12:00:51 -08:00
d2cc98b04d deps: fork vbsp 2025-02-26 11:59:59 -08:00
20 changed files with 886 additions and 632 deletions

493
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -982,10 +982,7 @@ impl PhysicsContext<'_>{
impl PhysicsData{
/// use with caution, this is the only non-instruction way to mess with physics
pub fn generate_models(&mut self,map:&map::CompleteMap){
let mut modes=map.modes.clone();
for mode in &mut modes.modes{
mode.denormalize_data();
}
let mut modes=map.modes.clone().denormalize();
let mut used_contact_attributes=Vec::new();
let mut used_intersect_attributes=Vec::new();
@ -1814,14 +1811,18 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
},
Instruction::Mode(ModeInstruction::Restart(mode_id))=>{
//teleport to mode start zone
let mut spawn_point=vec3::ZERO;
let mode=data.modes.get_mode(mode_id);
let spawn_point=mode.and_then(|mode|
if let Some(mode)=mode{
// set style
state.style=mode.get_style().clone();
//TODO: spawn at the bottom of the start zone plus the hitbox size
//TODO: set camera andles to face the same way as the start zone
data.models.get_model_transform(mode.get_start().into()).map(|transform|
transform.vertex.translation
)
).unwrap_or(vec3::ZERO);
//TODO: set camera angles to face the same way as the start zone
if let Some(transform)=data.models.get_model_transform(mode.get_start()){
// NOTE: this value may be 0,0,0 on source maps
spawn_point=transform.vertex.translation;
}
}
set_position(spawn_point,&mut state.move_state,&mut state.body,&mut state.touching,&mut state.run,&mut state.mode_state,mode,&data.models,&data.hitbox_mesh,&data.bvh,&state.style,&state.camera,&state.input_state,state.time);
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::ZERO);
state.set_move_state(data,MoveState::Air);
@ -1831,6 +1832,9 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
Instruction::Mode(ModeInstruction::Spawn(mode_id,stage_id))=>{
//spawn at a particular stage
if let Some(mode)=data.modes.get_mode(mode_id){
// set style
state.style=mode.get_style().clone();
// teleport to stage in mode
if let Some(stage)=mode.get_stage(stage_id){
let _=teleport_to_spawn(
stage.spawn(),

@ -1,5 +1,7 @@
use std::{io::{Cursor,Read},path::Path};
use std::io::Cursor;
use std::path::Path;
use std::time::Instant;
use strafesnet_physics::physics::{PhysicsData,PhysicsState,PhysicsContext};
@ -37,9 +39,7 @@ impl From<strafesnet_snf::bot::Error> for ReplayError{
}
fn read_entire_file(path:impl AsRef<Path>)->Result<Cursor<Vec<u8>>,std::io::Error>{
let mut file=std::fs::File::open(path)?;
let mut data=Vec::new();
file.read_to_end(&mut data)?;
let data=std::fs::read(path)?;
Ok(Cursor::new(data))
}
@ -72,7 +72,12 @@ enum DeterminismResult{
Deterministic,
NonDeterministic,
}
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->DeterminismResult{
struct SegmentResult{
determinism:DeterminismResult,
ticks:u64,
nanoseconds:u64,
}
fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsData)->SegmentResult{
// create default physics state
let mut physics_deterministic=PhysicsState::default();
// create a second physics state
@ -82,6 +87,9 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
println!("simulating...");
let mut non_idle_count=0;
let instruction_count=bot.instructions.len();
let start=Instant::now();
for (i,ins) in bot.instructions.into_iter().enumerate(){
let state_deterministic=physics_deterministic.clone();
@ -97,6 +105,7 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
let b0=physics_deterministic.camera_body();
let b1=physics_filtered.camera_body();
if b0.position!=b1.position{
let nanoseconds=start.elapsed().as_nanos() as u64;
println!("desync at instruction #{}",i);
println!("non idle instructions completed={non_idle_count}");
println!("instruction #{i}={:?}",other);
@ -104,11 +113,16 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
println!("filtered state0:\n{state_filtered:?}");
println!("deterministic state1:\n{:?}",physics_deterministic);
println!("filtered state1:\n{:?}",physics_filtered);
return DeterminismResult::NonDeterministic;
return SegmentResult{
determinism:DeterminismResult::NonDeterministic,
ticks:1+i as u64+non_idle_count,
nanoseconds,
};
}
},
}
}
let nanoseconds=start.elapsed().as_nanos() as u64;
match physics_deterministic.get_finish_time(){
Some(time)=>println!("[with idle] finish time:{}",time),
None=>println!("[with idle] simulation did not end in finished state"),
@ -117,9 +131,14 @@ fn segment_determinism(bot:strafesnet_snf::bot::Segment,physics_data:&PhysicsDat
Some(time)=>println!("[filtered] finish time:{}",time),
None=>println!("[filtered] simulation did not end in finished state"),
}
DeterminismResult::Deterministic
SegmentResult{
determinism:DeterminismResult::Deterministic,
ticks:instruction_count as u64+non_idle_count,
nanoseconds,
}
}
type ThreadResult=Result<Option<DeterminismResult>,ReplayError>;
type ThreadResult=Result<Option<SegmentResult>,ReplayError>;
fn read_and_run(file_path:std::path::PathBuf,physics_data:&PhysicsData)->ThreadResult{
let data=read_entire_file(file_path.as_path())?;
let bot=strafesnet_snf::read_bot(data)?.read_all()?;
@ -198,10 +217,18 @@ fn test_determinism()->Result<(),ReplayError>{
invalid:u32,
error:u32,
}
let mut nanoseconds=0;
let mut ticks=0;
let Totals{deterministic,nondeterministic,invalid,error}=thread_results.into_iter().fold(Totals::default(),|mut totals,result|{
match result{
Ok(Some(DeterminismResult::Deterministic))=>totals.deterministic+=1,
Ok(Some(DeterminismResult::NonDeterministic))=>totals.nondeterministic+=1,
Ok(Some(segment_result))=>{
ticks+=segment_result.ticks;
nanoseconds+=segment_result.nanoseconds;
match segment_result.determinism{
DeterminismResult::Deterministic=>totals.deterministic+=1,
DeterminismResult::NonDeterministic=>totals.nondeterministic+=1,
}
},
Ok(None)=>totals.invalid+=1,
Err(_)=>totals.error+=1,
}
@ -212,6 +239,7 @@ fn test_determinism()->Result<(),ReplayError>{
println!("nondeterministic={nondeterministic}");
println!("invalid={invalid}");
println!("error={error}");
println!("average ticks/s per core: {}",ticks*1_000_000_000/nanoseconds);
assert!(nondeterministic==0);
assert!(invalid==0);

@ -13,7 +13,7 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
glam = "0.30.0"
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }
vbsp = { version = "0.7.0-codegen5", registry = "strafesnet", default-features = false }
vbsp-entities = { version = "0.1.0", registry = "strafesnet", default-features = false, features = ["css"]}
vbsp = "0.8.0"
vbsp-entities-css = "0.6.0"
vmdl = "0.2.0"
vpk = "0.2.0"
vpk = "0.3.0"

@ -240,7 +240,7 @@ pub fn brush_to_mesh(bsp:&vbsp::Bsp,brush:&vbsp::Brush)->Result<model::Mesh,Brus
// The so-called tumor brushes have TRIGGER bit set
// but also ignore visleaf hint and skip sides
const TUMOR:vbsp::TextureFlags=vbsp::TextureFlags::HINT.union(vbsp::TextureFlags::SKIP).union(vbsp::TextureFlags::TRIGGER);
if let Some(texture_info)=bsp.textures_info.get(side.texture_info as usize){
if let Some(texture_info)=bsp.texture_info(side.texture_info as usize){
if texture_info.flags.intersects(TUMOR){
return None;
}

@ -1,11 +1,12 @@
use std::borrow::Cow;
use vbsp_entities::css::Entity;
use vbsp_entities_css::Entity;
use strafesnet_common::{map,model,integer,gameplay_attributes};
use strafesnet_common::{map,model,integer,gameplay_attributes as attr};
use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader};
use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
use strafesnet_common::gameplay_modes::{NormalizedMode,NormalizedModes,Mode,Stage};
use crate::valve_transform;
@ -41,7 +42,7 @@ fn add_brush<'a>(
model:&'a str,
origin:vbsp::Vector,
rendercolor:vbsp::Color,
attributes:gameplay_attributes::CollisionAttributesId,
attributes:attr::CollisionAttributesId,
){
let transform=integer::Planar64Affine3::from_translation(
valve_transform(origin.into())
@ -82,29 +83,60 @@ pub fn convert<'a>(
)->PartialMap1{
let bsp=bsp.as_ref();
//figure out real attributes later
let mut unique_attributes=Vec::new();
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
unique_attributes.push(gameplay_attributes::CollisionAttributes::contact_default());
unique_attributes.push(gameplay_attributes::CollisionAttributes::intersect_default());
const ATTRIBUTE_DECORATION:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0);
const ATTRIBUTE_CONTACT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(1);
const ATTRIBUTE_INTERSECT_DEFAULT:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(2);
let unique_attributes=vec![
attr::CollisionAttributes::Decoration,
attr::CollisionAttributes::contact_default(),
attr::CollisionAttributes::intersect_default(),
// ladder
attr::CollisionAttributes::Contact(
attr::ContactAttributes{
contacting:attr::ContactingAttributes{
contact_behaviour:Some(attr::ContactingBehaviour::Ladder(
attr::ContactingLadder{sticky:true}
))
},
general:attr::GeneralAttributes::default(),
}
),
// water
attr::CollisionAttributes::Intersect(
attr::IntersectAttributes{
intersecting:attr::IntersectingAttributes{
water:Some(attr::IntersectingWater{
viscosity:integer::Planar64::ONE,
density:integer::Planar64::ONE,
velocity:integer::vec3::ZERO,
}),
},
general:attr::GeneralAttributes::default(),
}
),
];
const ATTRIBUTE_DECORATION:attr::CollisionAttributesId=attr::CollisionAttributesId::new(0);
const ATTRIBUTE_CONTACT_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(1);
const ATTRIBUTE_INTERSECT_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(2);
const ATTRIBUTE_LADDER_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(3);
const ATTRIBUTE_WATER_DEFAULT:attr::CollisionAttributesId=attr::CollisionAttributesId::new(4);
//declare all prop models to Loader
let mut prop_models=bsp.static_props().map(|prop|{
const DEG_TO_RAD:f32=std::f32::consts::TAU/360.0;
//get or create mesh_id
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
let placement=prop.as_prop_placement();
model::Model{
mesh:mesh_id,
attributes:ATTRIBUTE_DECORATION,
transform:integer::Planar64Affine3::new(
integer::mat3::try_from_f32_array_2d((
glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale))
integer::mat3::try_from_f32_array_2d(
//TODO: figure this out
*glam::Mat3A::from_quat(glam::Quat::from_array(placement.rotation.into()))
).to_cols_array_2d()).unwrap(),
valve_transform(placement.origin.into()),
glam::Mat3A::from_euler(
glam::EulerRot::XYZ,
prop.angles.pitch*DEG_TO_RAD,
prop.angles.yaw*DEG_TO_RAD,
prop.angles.roll*DEG_TO_RAD
).to_cols_array_2d()
).unwrap(),
valve_transform(prop.origin.into()),
),
color:glam::Vec4::ONE,
}
@ -177,52 +209,204 @@ pub fn convert<'a>(
color:glam::Vec4::W,
});
// THE CUBE OF DESTINY
let destination_mesh_id=model::MeshId::new(world_meshes.len() as u32);
world_meshes.push(crate::brush::unit_cube());
const WHITE:vbsp::Color=vbsp::Color{r:255,g:255,b:255};
const ENTITY_ATTRIBUTE:attr::CollisionAttributesId=ATTRIBUTE_DECORATION;
const ENTITY_TRIGGER_ATTRIBUTE:attr::CollisionAttributesId=ATTRIBUTE_INTERSECT_DEFAULT;
for raw_ent in &bsp.entities{
macro_rules! ent_brush_default{
($entity:ident)=>{
add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,$entity.rendercolor,ENTITY_ATTRIBUTE)
};
} macro_rules! ent_brush_prop{
($entity:ident)=>{
add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_ATTRIBUTE)
};
}
macro_rules! ent_brush_trigger{
($entity:ident)=>{
add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,$entity.model,$entity.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE)
};
}
match raw_ent.parse(){
Ok(Entity::Cycler(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::EnvSprite(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.parse().unwrap_or(WHITE),ATTRIBUTE_DECORATION),
Ok(Entity::FuncBreakable(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncBrush(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncButton(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncDoor(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncDoorRotating(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::AmbientGeneric(_ambient_generic))=>(),
Ok(Entity::Cycler(brush))=>ent_brush_default!(brush),
Ok(Entity::EnvBeam(_env_beam))=>(),
Ok(Entity::EnvBubbles(_env_bubbles))=>(),
Ok(Entity::EnvDetailController(_env_detail_controller))=>(),
Ok(Entity::EnvEmbers(_env_embers))=>(),
Ok(Entity::EnvEntityMaker(_env_entity_maker))=>(),
Ok(Entity::EnvExplosion(_env_explosion))=>(),
Ok(Entity::EnvFade(_env_fade))=>(),
Ok(Entity::EnvFire(_env_fire))=>(),
Ok(Entity::EnvFireTrail(_env_fire_trail))=>(),
Ok(Entity::EnvFiresource(_env_firesource))=>(),
Ok(Entity::EnvFogController(_env_fog_controller))=>(),
Ok(Entity::EnvHudhint(_env_hudhint))=>(),
Ok(Entity::EnvLaser(_env_laser))=>(),
Ok(Entity::EnvLightglow(_env_lightglow))=>(),
Ok(Entity::EnvPhysexplosion(_env_physexplosion))=>(),
Ok(Entity::EnvProjectedtexture(_env_projectedtexture))=>(),
Ok(Entity::EnvScreenoverlay(_env_screenoverlay))=>(),
Ok(Entity::EnvShake(_env_shake))=>(),
Ok(Entity::EnvShooter(_env_shooter))=>(),
Ok(Entity::EnvSmokestack(_env_smokestack))=>(),
Ok(Entity::EnvSoundscape(_env_soundscape))=>(),
Ok(Entity::EnvSoundscapeProxy(_env_soundscape_proxy))=>(),
Ok(Entity::EnvSoundscapeTriggerable(_env_soundscape_triggerable))=>(),
Ok(Entity::EnvSpark(_env_spark))=>(),
Ok(Entity::EnvSprite(brush))=>ent_brush_default!(brush),
Ok(Entity::EnvSpritetrail(_env_spritetrail))=>(),
Ok(Entity::EnvSteam(_env_steam))=>(),
Ok(Entity::EnvSun(_env_sun))=>(),
Ok(Entity::EnvTonemapController(_env_tonemap_controller))=>(),
Ok(Entity::EnvWind(_env_wind))=>(),
// trigger_teleport.filtername probably has to do with one of these
Ok(Entity::FilterActivatorClass(_filter_activator_class))=>(),
Ok(Entity::FilterActivatorName(_filter_activator_name))=>(),
Ok(Entity::FilterDamageType(_filter_damage_type))=>(),
Ok(Entity::FilterMulti(_filter_multi))=>(),
Ok(Entity::FuncAreaportal(_func_areaportal))=>(),
Ok(Entity::FuncAreaportalwindow(_func_areaportalwindow))=>(),
Ok(Entity::FuncBombTarget(_func_bomb_target))=>(),
Ok(Entity::FuncBreakable(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncBreakableSurf(_func_breakable_surf))=>(),
Ok(Entity::FuncBrush(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncButton(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncBuyzone(_func_buyzone))=>(),
Ok(Entity::FuncClipVphysics(_func_clip_vphysics))=>(),
Ok(Entity::FuncConveyor(_func_conveyor))=>(),
// FuncDoor is Platform
Ok(Entity::FuncDoor(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncDoorRotating(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncDustcloud(_func_dustcloud))=>(),
Ok(Entity::FuncDustmotes(_func_dustmotes))=>(),
Ok(Entity::FuncFishPool(_func_fish_pool))=>(),
Ok(Entity::FuncFootstepControl(_func_footstep_control))=>(),
Ok(Entity::FuncHostageRescue(_func_hostage_rescue))=>(),
Ok(Entity::FuncIllusionary(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncMonitor(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncMovelinear(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncPhysbox(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncPhysboxMultiplayer(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncRotButton(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::FuncRotating(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncTracktrain(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncTrain(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncWall(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncWallToggle(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ATTRIBUTE_DECORATION),
Ok(Entity::FuncWaterAnalog(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.unwrap_or(WHITE),ATTRIBUTE_DECORATION),
Ok(Entity::PropDoorRotating(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::PropDynamic(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::PropDynamicOverride(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::PropPhysics(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::PropPhysicsMultiplayer(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::PropPhysicsOverride(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::PropRagdoll(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerGravity(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerHurt(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerLook(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerMultiple(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerOnce(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerProximity(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerPush(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerSoundscape(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerTeleport(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerVphysicsMotion(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::TriggerWind(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,WHITE,ATTRIBUTE_DECORATION),
Ok(Entity::InfoPlayerCounterterrorist(spawn))=>{
found_spawn=Some(valve_transform(spawn.origin.into()));
},
Ok(Entity::InfoPlayerTerrorist(spawn))=>{
found_spawn=Some(valve_transform(spawn.origin.into()));
},
Ok(Entity::FuncLod(_func_lod))=>(),
Ok(Entity::FuncMonitor(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncMovelinear(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncOccluder(_func_occluder))=>(),
Ok(Entity::FuncPhysbox(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncPhysboxMultiplayer(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncPrecipitation(_func_precipitation))=>(),
Ok(Entity::FuncRotButton(brush))=>ent_brush_prop!(brush),
Ok(Entity::FuncRotating(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncSmokevolume(_func_smokevolume))=>(),
Ok(Entity::FuncTracktrain(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncTrain(brush))=>ent_brush_default!(brush),
Ok(Entity::FuncWall(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE),
Ok(Entity::FuncWallToggle(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin.unwrap_or_default(),brush.rendercolor,ENTITY_ATTRIBUTE),
Ok(Entity::FuncWaterAnalog(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model,brush.origin,brush.rendercolor.unwrap_or(WHITE),ENTITY_ATTRIBUTE),
Ok(Entity::GamePlayerEquip(_game_player_equip))=>(),
Ok(Entity::GameText(_game_text))=>(),
Ok(Entity::GameUi(_game_ui))=>(),
Ok(Entity::GameWeaponManager(_game_weapon_manager))=>(),
Ok(Entity::HostageEntity(_hostage_entity))=>(),
Ok(Entity::InfoCameraLink(_info_camera_link))=>(),
Ok(Entity::InfoLadder(_info_ladder))=>(),
Ok(Entity::InfoLightingRelative(_info_lighting_relative))=>(),
Ok(Entity::InfoMapParameters(_info_map_parameters))=>(),
Ok(Entity::InfoNode(_info_node))=>(),
Ok(Entity::InfoNodeHint(_info_node_hint))=>(),
Ok(Entity::InfoParticleSystem(_info_particle_system))=>(),
Ok(Entity::InfoPlayerCounterterrorist(spawn))=>found_spawn=Some(spawn.origin),
Ok(Entity::InfoPlayerLogo(_info_player_logo))=>(),
Ok(Entity::InfoPlayerStart(_info_player_start))=>(),
Ok(Entity::InfoPlayerTerrorist(spawn))=>found_spawn=Some(spawn.origin),
Ok(Entity::InfoTarget(_info_target))=>(),
// InfoTeleportDestination is Spawn#
Ok(Entity::InfoTeleportDestination(_info_teleport_destination))=>(),
Ok(Entity::Infodecal(_infodecal))=>(),
Ok(Entity::KeyframeRope(_keyframe_rope))=>(),
Ok(Entity::Light(_light))=>(),
Ok(Entity::LightEnvironment(_light_environment))=>(),
Ok(Entity::LightSpot(_light_spot))=>(),
Ok(Entity::LogicAuto(_logic_auto))=>(),
Ok(Entity::LogicBranch(_logic_branch))=>(),
Ok(Entity::LogicCase(_logic_case))=>(),
Ok(Entity::LogicCompare(_logic_compare))=>(),
Ok(Entity::LogicMeasureMovement(_logic_measure_movement))=>(),
Ok(Entity::LogicRelay(_logic_relay))=>(),
Ok(Entity::LogicTimer(_logic_timer))=>(),
Ok(Entity::MathCounter(_math_counter))=>(),
Ok(Entity::MoveRope(_move_rope))=>(),
Ok(Entity::PathTrack(_path_track))=>(),
Ok(Entity::PhysBallsocket(_phys_ballsocket))=>(),
Ok(Entity::PhysConstraint(_phys_constraint))=>(),
Ok(Entity::PhysConstraintsystem(_phys_constraintsystem))=>(),
Ok(Entity::PhysHinge(_phys_hinge))=>(),
Ok(Entity::PhysKeepupright(_phys_keepupright))=>(),
Ok(Entity::PhysLengthconstraint(_phys_lengthconstraint))=>(),
Ok(Entity::PhysPulleyconstraint(_phys_pulleyconstraint))=>(),
Ok(Entity::PhysRagdollconstraint(_phys_ragdollconstraint))=>(),
Ok(Entity::PhysRagdollmagnet(_phys_ragdollmagnet))=>(),
Ok(Entity::PhysThruster(_phys_thruster))=>(),
Ok(Entity::PhysTorque(_phys_torque))=>(),
Ok(Entity::PlayerSpeedmod(_player_speedmod))=>(),
Ok(Entity::PlayerWeaponstrip(_player_weaponstrip))=>(),
Ok(Entity::PointCamera(_point_camera))=>(),
Ok(Entity::PointClientcommand(_point_clientcommand))=>(),
Ok(Entity::PointDevshotCamera(_point_devshot_camera))=>(),
Ok(Entity::PointServercommand(_point_servercommand))=>(),
Ok(Entity::PointSpotlight(_point_spotlight))=>(),
Ok(Entity::PointSurroundtest(_point_surroundtest))=>(),
Ok(Entity::PointTemplate(_point_template))=>(),
Ok(Entity::PointTesla(_point_tesla))=>(),
Ok(Entity::PointViewcontrol(_point_viewcontrol))=>(),
Ok(Entity::PropDoorRotating(brush))=>ent_brush_prop!(brush),
Ok(Entity::PropDynamic(brush))=>ent_brush_prop!(brush),
Ok(Entity::PropDynamicOverride(brush))=>ent_brush_prop!(brush),
Ok(Entity::PropPhysics(brush))=>ent_brush_prop!(brush),
Ok(Entity::PropPhysicsMultiplayer(brush))=>ent_brush_prop!(brush),
Ok(Entity::PropPhysicsOverride(brush))=>ent_brush_prop!(brush),
Ok(Entity::PropRagdoll(brush))=>ent_brush_prop!(brush),
Ok(Entity::ShadowControl(_shadow_control))=>(),
Ok(Entity::SkyCamera(_sky_camera))=>(),
Ok(Entity::TriggerGravity(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerHurt(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerLook(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerMultiple(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE),
Ok(Entity::TriggerOnce(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerProximity(brush))=>ent_brush_trigger!(brush),
// TriggerPush is booster
Ok(Entity::TriggerPush(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerSoundscape(brush))=>ent_brush_trigger!(brush),
// TriggerTeleport is Trigger#
Ok(Entity::TriggerTeleport(brush))=>add_brush(mesh_deferred_loader,&mut world_models,&mut prop_models,brush.model.unwrap_or_default(),brush.origin,WHITE,ENTITY_TRIGGER_ATTRIBUTE),
Ok(Entity::TriggerVphysicsMotion(brush))=>ent_brush_trigger!(brush),
Ok(Entity::TriggerWind(brush))=>ent_brush_trigger!(brush),
Ok(Entity::WaterLodControl(_water_lod_control))=>(),
Ok(Entity::WeaponAk47(_weapon_ak47))=>(),
Ok(Entity::WeaponAwp(_weapon_awp))=>(),
Ok(Entity::WeaponDeagle(_weapon_deagle))=>(),
Ok(Entity::WeaponElite(_weapon_elite))=>(),
Ok(Entity::WeaponFamas(_weapon_famas))=>(),
Ok(Entity::WeaponFiveseven(_weapon_fiveseven))=>(),
Ok(Entity::WeaponFlashbang(_weapon_flashbang))=>(),
Ok(Entity::WeaponG3sg1(_weapon_g3sg1))=>(),
Ok(Entity::WeaponGlock(_weapon_glock))=>(),
Ok(Entity::WeaponHegrenade(_weapon_hegrenade))=>(),
Ok(Entity::WeaponKnife(_weapon_knife))=>(),
Ok(Entity::WeaponM249(_weapon_m249))=>(),
Ok(Entity::WeaponM3(_weapon_m3))=>(),
Ok(Entity::WeaponM4a1(_weapon_m4a1))=>(),
Ok(Entity::WeaponMac10(_weapon_mac10))=>(),
Ok(Entity::WeaponP228(_weapon_p228))=>(),
Ok(Entity::WeaponP90(_weapon_p90))=>(),
Ok(Entity::WeaponScout(_weapon_scout))=>(),
Ok(Entity::WeaponSg550(_weapon_sg550))=>(),
Ok(Entity::WeaponSmokegrenade(_weapon_smokegrenade))=>(),
Ok(Entity::WeaponTmp(_weapon_tmp))=>(),
Ok(Entity::WeaponUmp45(_weapon_ump45))=>(),
Ok(Entity::WeaponUsp(_weapon_usp))=>(),
Ok(Entity::WeaponXm1014(_weapon_xm1014))=>(),
Ok(Entity::Worldspawn(_worldspawn))=>(),
Err(e)=>{
println!("Bsp Entity parse error: {e}");
},
@ -232,9 +416,27 @@ pub fn convert<'a>(
// physics models
for brush in &bsp.brushes{
if !brush.flags.contains(vbsp::BrushFlags::SOLID){
const RELEVANT:vbsp::BrushFlags=
vbsp::BrushFlags::SOLID
.union(vbsp::BrushFlags::PLAYERCLIP)
.union(vbsp::BrushFlags::WATER)
.union(vbsp::BrushFlags::MOVEABLE)
.union(vbsp::BrushFlags::LADDER);
if !brush.flags.intersects(RELEVANT){
continue;
}
let is_ladder=brush.flags.contains(vbsp::BrushFlags::LADDER);
let is_water=brush.flags.contains(vbsp::BrushFlags::WATER);
let attributes=match (is_ladder,is_water){
(true,false)=>ATTRIBUTE_LADDER_DEFAULT,
(false,true)=>ATTRIBUTE_WATER_DEFAULT,
(false,false)=>ATTRIBUTE_CONTACT_DEFAULT,
(true,true)=>{
// water ladder? wtf
println!("brush is a water ladder o_o defaulting to ladder");
ATTRIBUTE_LADDER_DEFAULT
}
};
let mesh_result=crate::brush::brush_to_mesh(bsp,brush);
match mesh_result{
Ok(mesh)=>{
@ -242,7 +444,7 @@ pub fn convert<'a>(
world_meshes.push(mesh);
world_models.push(model::Model{
mesh:mesh_id,
attributes:ATTRIBUTE_CONTACT_DEFAULT,
attributes,
transform:integer::Planar64Affine3::new(
integer::mat3::identity(),
integer::vec3::ZERO,
@ -256,27 +458,24 @@ pub fn convert<'a>(
let mut modes_list=Vec::new();
if let Some(spawn_point)=found_spawn{
// create a new mesh
let mesh_id=model::MeshId::new(world_meshes.len() as u32);
world_meshes.push(crate::brush::unit_cube());
// create a new model
let model_id=model::ModelId::new(world_models.len() as u32);
world_models.push(model::Model{
mesh:mesh_id,
mesh:destination_mesh_id,
attributes:ATTRIBUTE_INTERSECT_DEFAULT,
transform:integer::Planar64Affine3::from_translation(spawn_point),
transform:integer::Planar64Affine3::from_translation(valve_transform(spawn_point.into())),
color:glam::Vec4::W,
});
let first_stage=strafesnet_common::gameplay_modes::Stage::empty(model_id);
let main_mode=strafesnet_common::gameplay_modes::Mode::new(
let first_stage=Stage::empty(model_id);
let main_mode=Mode::new(
strafesnet_common::gameplay_style::StyleModifiers::source_bhop(),
model_id,
std::collections::HashMap::new(),
vec![first_stage],
std::collections::HashMap::new(),
);
modes_list.push(main_mode);
modes_list.push(NormalizedMode::new(main_mode));
}
PartialMap1{
@ -284,17 +483,17 @@ pub fn convert<'a>(
world_meshes,
prop_models,
world_models,
modes:strafesnet_common::gameplay_modes::Modes::new(modes_list),
modes:NormalizedModes::new(modes_list),
}
}
//partially constructed map types
pub struct PartialMap1{
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
attributes:Vec<attr::CollisionAttributes>,
prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>,
modes:strafesnet_common::gameplay_modes::Modes,
modes:NormalizedModes,
}
impl PartialMap1{
pub fn add_prop_meshes<'a>(
@ -312,12 +511,12 @@ impl PartialMap1{
}
}
pub struct PartialMap2{
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
attributes:Vec<attr::CollisionAttributes>,
prop_meshes:Vec<(model::MeshId,model::Mesh)>,
prop_models:Vec<model::Model>,
world_meshes:Vec<model::Mesh>,
world_models:Vec<model::Model>,
modes:strafesnet_common::gameplay_modes::Modes,
modes:NormalizedModes,
}
impl PartialMap2{
pub fn add_render_configs_and_textures(

@ -7,46 +7,57 @@ pub struct Aabb{
}
impl Default for Aabb{
#[inline]
fn default()->Self{
Self{min:vec3::MAX,max:vec3::MIN}
}
}
impl Aabb{
#[inline]
pub const fn new(min:Planar64Vec3,max:Planar64Vec3)->Self{
Self{min,max}
}
#[inline]
pub const fn max(&self)->Planar64Vec3{
self.max
}
#[inline]
pub const fn min(&self)->Planar64Vec3{
self.min
}
#[inline]
pub fn grow(&mut self,point:Planar64Vec3){
self.min=self.min.min(point);
self.max=self.max.max(point);
}
#[inline]
pub fn join(&mut self,aabb:&Aabb){
self.min=self.min.min(aabb.min);
self.max=self.max.max(aabb.max);
}
#[inline]
pub fn inflate(&mut self,hs:Planar64Vec3){
self.min-=hs;
self.max+=hs;
}
#[inline]
pub fn contains(&self,point:Planar64Vec3)->bool{
let bvec=self.min.lt(point)&point.lt(self.max);
bvec.all()
}
#[inline]
pub fn intersects(&self,aabb:&Aabb)->bool{
let bvec=self.min.lt(aabb.max)&aabb.min.lt(self.max);
bvec.all()
}
#[inline]
pub fn size(&self)->Planar64Vec3{
self.max-self.min
}
#[inline]
pub fn center(&self)->Planar64Vec3{
self.min+(self.max-self.min)>>1
self.min.map_zip(self.max,|(min,max)|min.midpoint(max))
}
//probably use floats for area & volume because we don't care about precision
// pub fn area_weight(&self)->f32{

@ -1,7 +1,6 @@
use std::collections::{HashSet,HashMap};
use crate::model::ModelId;
use crate::gameplay_style;
use crate::updatable::Updatable;
#[derive(Clone)]
pub struct StageElement{
@ -128,18 +127,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{
@ -211,34 +198,47 @@ impl Mode{
pub fn push_stage(&mut self,stage:Stage){
self.stages.push(stage)
}
pub fn get_stage_mut(&mut self,stage:StageId)->Option<&mut Stage>{
self.stages.get_mut(stage.0 as usize)
pub fn get_stage_mut(&mut self,StageId(stage_id):StageId)->Option<&mut Stage>{
self.stages.get_mut(stage_id as usize)
}
pub fn get_spawn_model_id(&self,stage:StageId)->Option<ModelId>{
self.stages.get(stage.0 as usize).map(|s|s.spawn)
pub fn get_spawn_model_id(&self,StageId(stage_id):StageId)->Option<ModelId>{
self.stages.get(stage_id as usize).map(|s|s.spawn)
}
pub fn get_zone(&self,model_id:ModelId)->Option<&Zone>{
self.zones.get(&model_id)
}
pub fn get_stage(&self,stage_id:StageId)->Option<&Stage>{
self.stages.get(stage_id.0 as usize)
pub fn get_stage(&self,StageId(stage_id):StageId)->Option<&Stage>{
self.stages.get(stage_id as usize)
}
pub fn get_element(&self,model_id:ModelId)->Option<&StageElement>{
self.elements.get(&model_id)
}
}
#[derive(Clone)]
pub struct NormalizedMode(Mode);
impl NormalizedMode{
pub fn new(mode:Mode)->Self{
Self(mode)
}
pub fn into_inner(self)->Mode{
let Self(mode)=self;
mode
}
//TODO: put this in the SNF
pub fn denormalize_data(&mut self){
pub fn denormalize(self)->Mode{
let NormalizedMode(mut mode)=self;
//expand and index normalized data
self.zones.insert(self.start,Zone::Start);
for (stage_id,stage) in self.stages.iter().enumerate(){
self.elements.insert(stage.spawn,StageElement{
mode.zones.insert(mode.start,Zone::Start);
for (stage_id,stage) in mode.stages.iter().enumerate(){
mode.elements.insert(stage.spawn,StageElement{
stage_id:StageId(stage_id as u32),
force:false,
behaviour:StageElementBehaviour::SpawnAt,
jump_limit:None,
});
for (_,&model) in &stage.ordered_checkpoints{
self.elements.insert(model,StageElement{
mode.elements.insert(model,StageElement{
stage_id:StageId(stage_id as u32),
force:false,
behaviour:StageElementBehaviour::Checkpoint,
@ -246,7 +246,7 @@ impl Mode{
});
}
for &model in &stage.unordered_checkpoints{
self.elements.insert(model,StageElement{
mode.elements.insert(model,StageElement{
stage_id:StageId(stage_id as u32),
force:false,
behaviour:StageElementBehaviour::Checkpoint,
@ -254,53 +254,13 @@ 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);
}
mode
}
}
#[derive(Default,Clone)]
pub struct Modes{
pub modes:Vec<Mode>,
modes:Vec<Mode>,
}
impl Modes{
pub const fn new(modes:Vec<Mode>)->Self{
@ -314,19 +274,182 @@ impl Modes{
pub fn push_mode(&mut self,mode:Mode){
self.modes.push(mode)
}
pub fn get_mode(&self,mode:ModeId)->Option<&Mode>{
self.modes.get(mode.0 as usize)
pub fn get_mode(&self,ModeId(mode_id):ModeId)->Option<&Mode>{
self.modes.get(mode_id as usize)
}
}
pub struct ModesUpdate{
modes:HashMap<ModeId,ModeUpdate>,
#[derive(Clone)]
pub struct NormalizedModes{
modes:Vec<NormalizedMode>,
}
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){
mode.update(mode_update);
}
impl NormalizedModes{
pub fn new(modes:Vec<NormalizedMode>)->Self{
Self{modes}
}
pub fn len(&self)->usize{
self.modes.len()
}
pub fn denormalize(self)->Modes{
Modes{
modes:self.modes.into_iter().map(NormalizedMode::denormalize).collect(),
}
}
}
impl IntoIterator for NormalizedModes{
type Item=<Vec<NormalizedMode> as IntoIterator>::Item;
type IntoIter=<Vec<NormalizedMode> as IntoIterator>::IntoIter;
fn into_iter(self)->Self::IntoIter{
self.modes.into_iter()
}
}
#[derive(Default)]
pub struct StageUpdate{
//other behaviour models of this stage can have
ordered_checkpoints:HashMap<CheckpointId,ModelId>,
unordered_checkpoints:HashSet<ModelId>,
}
impl StageUpdate{
fn apply_to(self,stage:&mut Stage){
stage.ordered_checkpoints.extend(self.ordered_checkpoints);
stage.unordered_checkpoints.extend(self.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 ModeUpdate{
fn apply_to(self,mode:&mut Mode){
mode.zones.extend(self.zones);
if let Some((StageId(stage_id),stage_update))=self.stages{
if let Some(stage)=mode.stages.get_mut(stage_id as usize){
stage_update.apply_to(stage);
}
}
mode.elements.extend(self.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);
}
}
}
struct ModeBuilder{
mode:Mode,
stage_id_map: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_normalized(mut self)->NormalizedModes{
//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,mode_id_map):(Vec<ModeBuilder>,HashMap<ModeId,ModeId>)
=unique_modes.into_iter().enumerate()
.map(|(final_mode_id,(builder_mode_id,mut mode))|{
(
ModeBuilder{
stage_id_map: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_key(|&(StageId(stage_id),_)|stage_id);
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)=mode_id_map.get(&builder_mode_id){
if let Some(mode)=modes.get_mut(final_mode_id.get() as usize){
if let Some(&final_stage_id)=mode.stage_id_map.get(&builder_stage_id){
if let Some(stage)=mode.mode.get_stage_mut(final_stage_id){
stage_update.apply_to(stage);
}
}
}
}
}
//push mode updates
for (builder_mode_id,mut mode_update) in self.mode_updates{
if let Some(final_mode_id)=mode_id_map.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.stage_id_map.get(&StageId::new(builder_stage_id)).copied()
).unwrap_or(StageId::FIRST)
);
mode_update.apply_to(&mut mode.mode);
}
}
}
NormalizedModes::new(modes.into_iter().map(|mode_builder|NormalizedMode(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));
// }
}

@ -63,7 +63,7 @@ impl JumpImpulse{
velocity:Planar64Vec3,
jump_dir:Planar64Vec3,
gravity:&Planar64Vec3,
mass:Planar64,
_mass:Planar64,
)->Planar64Vec3{
match self{
&JumpImpulse::Time(time)=>velocity-(*gravity*time).map(|t|t.divide().fix_1()),
@ -78,7 +78,7 @@ impl JumpImpulse{
velocity-(*gravity*(radicand.sqrt().fix_2()+v_g)/gg).divide().fix_1()
},
&JumpImpulse::Linear(jump_speed)=>velocity+(jump_dir*jump_speed/jump_dir.length()).divide().fix_1(),
&JumpImpulse::Energy(energy)=>{
&JumpImpulse::Energy(_energy)=>{
//calculate energy
//let e=gravity.dot(velocity);
//add
@ -355,7 +355,7 @@ pub struct LadderSettings{
pub dot:Planar64,
}
impl LadderSettings{
pub const fn accel(&self,target_diff:Planar64Vec3,gravity:Planar64Vec3)->Planar64{
pub const fn accel(&self,_target_diff:Planar64Vec3,_gravity:Planar64Vec3)->Planar64{
//TODO: fallible ladder accel
self.accelerate.accel
}
@ -529,12 +529,12 @@ impl StyleModifiers{
pub fn source_bhop()->Self{
Self{
controls_mask:Controls::all()-Controls::MoveUp-Controls::MoveDown,
controls_mask:Controls::all(),
controls_mask_state:Controls::all(),
strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(),
air_accel_limit:Some(Planar64::raw(150<<28)*100),
mv:(Planar64::raw(30)*VALVE_SCALE).fix_1(),
air_accel_limit:Some(Planar64::raw((150<<28)*100)),
mv:Planar64::raw(30<<28),
tick_rate:Ratio64::new(100,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
}),
jump:Some(JumpSettings{
@ -570,12 +570,12 @@ impl StyleModifiers{
}
pub fn source_surf()->Self{
Self{
controls_mask:Controls::all()-Controls::MoveUp-Controls::MoveDown,
controls_mask:Controls::all(),
controls_mask_state:Controls::all(),
strafe:Some(StrafeSettings{
enable:ControlsActivation::full_2d(),
air_accel_limit:Some((int(150)*66*VALVE_SCALE).fix_1()),
mv:(int(30)*VALVE_SCALE).fix_1(),
mv:Planar64::raw(30<<28),
tick_rate:Ratio64::new(66,AbsoluteTime::ONE_SECOND.get() as u64).unwrap(),
}),
jump:Some(JumpSettings{

@ -9,7 +9,6 @@ pub mod timer;
pub mod integer;
pub mod physics;
pub mod session;
pub mod updatable;
pub mod instruction;
pub mod gameplay_attributes;
pub mod gameplay_modes;

@ -4,7 +4,7 @@ use crate::gameplay_attributes;
//this is a temporary struct to try to get the code running again
//TODO: use snf::map::Region to update the data in physics and graphics instead of this
pub struct CompleteMap{
pub modes:gameplay_modes::Modes,
pub modes:gameplay_modes::NormalizedModes,
pub attributes:Vec<gameplay_attributes::CollisionAttributes>,
pub meshes:Vec<model::Mesh>,
pub models:Vec<model::Model>,

@ -1,57 +0,0 @@
// This whole thing should be a drive macro
pub trait Updatable<Updater>{
fn update(&mut self,update:Updater);
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
struct InnerId(u32);
#[derive(Clone)]
struct Inner{
id:InnerId,
enabled:bool,
}
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
struct OuterId(u32);
struct Outer{
id:OuterId,
inners:std::collections::HashMap<InnerId,Inner>,
}
enum Update<I,U>{
Insert(I),
Update(U),
Remove
}
struct InnerUpdate{
//#[updatable(Update)]
enabled:Option<bool>,
}
struct OuterUpdate{
//#[updatable(Insert,Update,Remove)]
inners:std::collections::HashMap<InnerId,Update<Inner,InnerUpdate>>,
//#[updatable(Update)]
//inners:std::collections::HashMap<InnerId,InnerUpdate>,
}
impl Updatable<InnerUpdate> for Inner{
fn update(&mut self,update:InnerUpdate){
if let Some(enabled)=update.enabled{
self.enabled=enabled;
}
}
}
impl Updatable<OuterUpdate> for Outer{
fn update(&mut self,update:OuterUpdate){
for (id,up) in update.inners{
match up{
Update::Insert(new_inner)=>self.inners.insert(id,new_inner),
Update::Update(inner_update)=>self.inners.get_mut(&id).map(|inner|{
let old=inner.clone();
inner.update(inner_update);
old
}),
Update::Remove=>self.inners.remove(&id),
};
}
}
}

@ -14,7 +14,7 @@ wide-mul=[]
zeroes=["dep:arrayvec"]
[dependencies]
bnum = "0.12.0"
bnum = "0.13.0"
arrayvec = { version = "0.7.6", optional = true }
paste = "1.0.15"
ratio_ops = { version = "0.1.0", path = "../ratio_ops", registry = "strafesnet", optional = true }

@ -33,6 +33,14 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
self.bits
}
#[inline]
pub const fn as_bits(&self)->&BInt<N>{
&self.bits
}
#[inline]
pub const fn as_bits_mut(&mut self)->&mut BInt<N>{
&mut self.bits
}
#[inline]
pub const fn raw_digit(value:i64)->Self{
let mut digits=[0u64;N];
digits[0]=value.abs() as u64;
@ -56,6 +64,10 @@ impl<const N:usize,const F:usize> Fixed<N,F>{
pub const fn abs(self)->Self{
Self::from_bits(self.bits.abs())
}
#[inline]
pub const fn midpoint(self,other:Self)->Self{
Self::from_bits(self.bits.midpoint(other.bits))
}
}
impl<const F:usize> Fixed<1,F>{
/// My old code called this function everywhere so let's provide it
@ -788,13 +800,10 @@ macro_rules! impl_not_const_generic{
let wide_self=self.[<fix_ $_2n>]();
//descend down the bits and check if flipping each bit would push the square over the input value
for shift in (0..=max_shift).rev(){
let new_result={
let mut bits=result.to_bits().to_bits();
bits.set_bit(shift,true);
Self::from_bits(BInt::from_bits(bits))
};
if new_result.[<wide_mul_ $n _ $n>](new_result)<=wide_self{
result=new_result;
result.as_bits_mut().as_bits_mut().set_bit(shift,true);
if wide_self<result.[<wide_mul_ $n _ $n>](result){
// put it back lol
result.as_bits_mut().as_bits_mut().set_bit(shift,false);
}
}
result

@ -84,13 +84,13 @@ where
fn ingest_faces2_lods3(
polygon_groups:&mut Vec<PolygonGroup>,
vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>,
faces:&Vec<rbx_mesh::mesh::Face2>,
lods:&Vec<rbx_mesh::mesh::Lod3>
faces:&[rbx_mesh::mesh::Face2],
lods:&[rbx_mesh::mesh::Lod3],
){
//faces have to be split into polygon groups based on lod
polygon_groups.extend(lods.windows(2).map(|lod_pair|
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|face|
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|rbx_mesh::mesh::Face2(v0,v1,v2)|
vec![vertex_id_map[&v0],vertex_id_map[&v1],vertex_id_map[&v2]]
).collect()))
))
}

@ -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::{NormalizedModes,Mode,ModeId,ModeUpdate,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"),
// }
@ -1009,7 +922,7 @@ impl PartialMap1<'_>{
PartialMap2{
meshes:self.primitive_meshes,
models,
modes:modes_builder.build(),
modes:modes_builder.build_normalized(),
attributes:unique_attributes,
}
}
@ -1018,7 +931,7 @@ impl PartialMap1<'_>{
pub struct PartialMap2{
meshes:Vec<model::Mesh>,
models:Vec<model::Model>,
modes:gameplay_modes::Modes,
modes:NormalizedModes,
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
}
impl PartialMap2{

@ -138,7 +138,7 @@ struct MapHeader{
//#[br(count=num_resources_external)]
//external_resources:Vec<ResourceExternalHeader>,
#[br(count=num_modes)]
modes:Vec<newtypes::gameplay_modes::Mode>,
modes:Vec<newtypes::gameplay_modes::NormalizedMode>,
#[br(count=num_attributes)]
attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>,
#[br(count=num_render_configs)]
@ -181,7 +181,7 @@ fn read_texture<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)
pub struct StreamableMap<R:BinReaderExt>{
file:crate::file::File<R>,
//this includes every platform... move the unconstrained datas to their appropriate data block?
modes:gameplay_modes::Modes,
modes:gameplay_modes::NormalizedModes,
//this is every possible attribute... need some sort of streaming system
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
//this is every possible render configuration... shaders and such... need streaming
@ -223,7 +223,7 @@ impl<R:BinReaderExt> StreamableMap<R>{
}
Ok(Self{
file,
modes:strafesnet_common::gameplay_modes::Modes::new(modes),
modes:strafesnet_common::gameplay_modes::NormalizedModes::new(modes),
attributes,
render_configs,
bvh:strafesnet_common::bvh::generate_bvh(bvh),
@ -430,13 +430,13 @@ pub fn write_map<W:BinWriterExt>(mut writer:W,map:strafesnet_common::map::Comple
num_spacial_blocks:spacial_blocks.len() as u32,
num_resource_blocks:resource_blocks.len() as u32,
//num_resources_external:0,
num_modes:map.modes.modes.len() as u32,
num_modes:map.modes.len() as u32,
num_attributes:map.attributes.len() as u32,
num_render_configs:map.render_configs.len() as u32,
spacial_blocks,
resource_blocks,
//external_resources:Vec::new(),
modes:map.modes.modes.into_iter().map(Into::into).collect(),
modes:map.modes.into_iter().map(Into::into).collect(),
attributes:map.attributes.into_iter().map(Into::into).collect(),
render_configs:map.render_configs.into_iter().map(Into::into).collect(),
};

@ -157,7 +157,7 @@ pub struct ModeHeader{
}
#[binrw::binrw]
#[brw(little)]
pub struct Mode{
pub struct NormalizedMode{
pub header:ModeHeader,
pub style:super::gameplay_style::StyleModifiers,
pub start:u32,
@ -179,10 +179,10 @@ impl std::fmt::Display for ModeError{
}
}
impl std::error::Error for ModeError{}
impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
impl TryInto<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
type Error=ModeError;
fn try_into(self)->Result<strafesnet_common::gameplay_modes::Mode,Self::Error>{
Ok(strafesnet_common::gameplay_modes::Mode::new(
fn try_into(self)->Result<strafesnet_common::gameplay_modes::NormalizedMode,Self::Error>{
Ok(strafesnet_common::gameplay_modes::NormalizedMode::new(strafesnet_common::gameplay_modes::Mode::new(
self.style.try_into().map_err(ModeError::StyleModifier)?,
strafesnet_common::model::ModelId::new(self.start),
self.zones.into_iter().map(|(model_id,zone)|
@ -192,12 +192,12 @@ impl TryInto<strafesnet_common::gameplay_modes::Mode> for Mode{
self.elements.into_iter().map(|(model_id,stage_element)|
Ok((strafesnet_common::model::ModelId::new(model_id),stage_element.try_into()?))
).collect::<Result<_,_>>().map_err(ModeError::StageElement)?,
))
)))
}
}
impl From<strafesnet_common::gameplay_modes::Mode> for Mode{
fn from(value:strafesnet_common::gameplay_modes::Mode)->Self{
let (style,start,zones,stages,elements)=value.into_inner();
impl From<strafesnet_common::gameplay_modes::NormalizedMode> for NormalizedMode{
fn from(value:strafesnet_common::gameplay_modes::NormalizedMode)->Self{
let (style,start,zones,stages,elements)=value.into_inner().into_inner();
Self{
header:ModeHeader{
zones:zones.len() as u32,

@ -25,11 +25,11 @@ strafesnet_rbx_loader = { version = "0.6.0", path = "../lib/rbx_loader", registr
strafesnet_snf = { version = "0.3.0", path = "../lib/snf", registry = "strafesnet" }
thiserror = "2.0.11"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] }
vbsp = { version = "0.7.0-codegen5", registry = "strafesnet", default-features = false }
vbsp-entities = { version = "0.1.0", registry = "strafesnet", default-features = false, features = ["css"]}
vbsp = "0.8.0"
vbsp-entities-css = "0.6.0"
vmdl = "0.2.0"
vmt-parser = "0.2.0"
vpk = "0.2.0"
vpk = "0.3.0"
vtf = "0.3.0"
#[profile.release]

@ -6,7 +6,7 @@ use futures::StreamExt;
use strafesnet_bsp_loader::loader::BspFinder;
use strafesnet_deferred_loader::loader::Loader;
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
use vbsp_entities::css::Entity;
use vbsp_entities_css::Entity;
#[derive(Subcommand)]
pub enum Commands{