Compare commits

...

1 Commits

Author SHA1 Message Date
eb40c1a0d0 add StrafeTickState to allow 0 time strafe events 2025-12-10 18:19:22 -08:00
2 changed files with 57 additions and 31 deletions

View File

@@ -488,8 +488,21 @@ impl StyleHelper for StyleModifiers{
} }
} }
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
struct StrafeTickState{
tick_number:u64,
}
impl StrafeTickState{
fn next_tick(&self,settings:&gameplay_style::StrafeSettings)->Time{
let n=self.tick_number as i128;
let ticks=settings.tick_rate.num() as i128;
let seconds=settings.tick_rate.den() as i128;
let time=n*seconds/ticks;
Time::from_nanos(time as i64)
}
}
#[derive(Clone,Debug)]
enum MoveState{ enum MoveState{
Air, Air(StrafeTickState),
Walk(ContactMoveState), Walk(ContactMoveState),
Ladder(ContactMoveState), Ladder(ContactMoveState),
#[expect(dead_code)] #[expect(dead_code)]
@@ -497,11 +510,21 @@ enum MoveState{
Fly, Fly,
} }
impl MoveState{ impl MoveState{
fn air(time:Time,settings:Option<&gameplay_style::StrafeSettings>)->Self{
let tick_number=if let Some(settings)=settings{
// let time=n*seconds/ticks;
let time=time.nanos() as i128;
let ticks=settings.tick_rate.num() as i128;
let seconds=settings.tick_rate.den() as i128;
(time*ticks/seconds) as u64
}else{0};
MoveState::Air(StrafeTickState{tick_number})
}
//call this after state.move_state is changed //call this after state.move_state is changed
fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
match self{ match self{
MoveState::Fly=>body.acceleration=vec3::zero(), MoveState::Fly=>body.acceleration=vec3::zero(),
MoveState::Air=>{ MoveState::Air(_)=>{
//calculate base acceleration //calculate base acceleration
let a=touching.base_acceleration(models,style,camera,input_state); let a=touching.base_acceleration(models,style,camera,input_state);
//set_acceleration clips according to contacts //set_acceleration clips according to contacts
@@ -513,7 +536,7 @@ impl MoveState{
//function to coerce &mut self into &self //function to coerce &mut self into &self
fn apply_to_body(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ fn apply_to_body(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
match self{ match self{
MoveState::Air=>(), MoveState::Air(_)=>(),
MoveState::Water=>(), MoveState::Water=>(),
MoveState::Fly=>{ MoveState::Fly=>{
//set velocity according to current control state //set velocity according to current control state
@@ -534,7 +557,7 @@ impl MoveState{
fn apply_input(&mut self,body:&Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ fn apply_input(&mut self,body:&Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){
match self{ match self{
MoveState::Fly MoveState::Fly
|MoveState::Air |MoveState::Air(_)
|MoveState::Water=>(), |MoveState::Water=>(),
MoveState::Walk(ContactMoveState{target,contact,jump_direction:_})=>{ MoveState::Walk(ContactMoveState{target,contact,jump_direction:_})=>{
if let Some(walk_settings)=&style.walk{ if let Some(walk_settings)=&style.walk{
@@ -559,13 +582,13 @@ impl MoveState{
MoveState::Walk(walk_state) MoveState::Walk(walk_state)
|MoveState::Ladder(walk_state) |MoveState::Ladder(walk_state)
=>Some(walk_state), =>Some(walk_state),
MoveState::Air MoveState::Air(_)
|MoveState::Water |MoveState::Water
|MoveState::Fly |MoveState::Fly
=>None, =>None,
} }
} }
fn next_move_instruction(&self,strafe:&Option<gameplay_style::StrafeSettings>,time:Time)->Option<TimedInstruction<InternalInstruction,Time>>{ fn next_move_instruction(&self,strafe:Option<&gameplay_style::StrafeSettings>)->Option<TimedInstruction<InternalInstruction,Time>>{
//check if you have a valid walk state and create an instruction //check if you have a valid walk state and create an instruction
match self{ match self{
MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{ MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{
@@ -577,9 +600,9 @@ impl MoveState{
|TransientAcceleration::Reached |TransientAcceleration::Reached
=>None, =>None,
} }
MoveState::Air=>strafe.as_ref().map(|strafe|{ MoveState::Air(strafe_tick_state)=>strafe.as_ref().map(|strafe|{
TimedInstruction{ TimedInstruction{
time:strafe.next_tick(time), time:strafe_tick_state.next_tick(strafe),
//only poll the physics if there is a before and after mouse event //only poll the physics if there is a before and after mouse event
instruction:InternalInstruction::StrafeTick instruction:InternalInstruction::StrafeTick
} }
@@ -607,7 +630,7 @@ impl MoveState{
//this function call reads the above state that was just set //this function call reads the above state that was just set
self.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state); self.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state);
} }
fn cull_velocity(&mut self,velocity:Planar64Vec3,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ fn cull_velocity(&mut self,velocity:Planar64Vec3,body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState,time:Time){
//TODO: be more precise about contacts //TODO: be more precise about contacts
if set_velocity_cull(body,touching,models,hitbox_mesh,velocity){ if set_velocity_cull(body,touching,models,hitbox_mesh,velocity){
// TODO do better // TODO do better
@@ -615,7 +638,7 @@ impl MoveState{
match self.get_walk_state(){ match self.get_walk_state(){
// did you stop touching the thing you were walking on? // did you stop touching the thing you were walking on?
Some(walk_state)=>if !touching.contains_contact(&walk_state.contact.convex_mesh_id){ Some(walk_state)=>if !touching.contains_contact(&walk_state.contact.convex_mesh_id){
self.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state); self.set_move_state(MoveState::air(time,style.strafe.as_ref()),body,touching,models,hitbox_mesh,style,camera,input_state);
}else{ }else{
// stopped touching something else while walking // stopped touching something else while walking
self.apply_enum_and_input_and_body(body,touching,models,hitbox_mesh,style,camera,input_state); self.apply_enum_and_input_and_body(body,touching,models,hitbox_mesh,style,camera,input_state);
@@ -890,7 +913,7 @@ impl Default for PhysicsState{
time:Time::ZERO, time:Time::ZERO,
style:StyleModifiers::default(), style:StyleModifiers::default(),
touching:TouchingState::default(), touching:TouchingState::default(),
move_state:MoveState::Air, move_state:MoveState::air(Time::ZERO,None),
camera:PhysicsCamera::default(), camera:PhysicsCamera::default(),
input_state:InputState::default(), input_state:InputState::default(),
_world:WorldState{}, _world:WorldState{},
@@ -935,10 +958,10 @@ impl PhysicsState{
*self=Self::default(); *self=Self::default();
} }
fn next_move_instruction(&self)->Option<TimedInstruction<InternalInstruction,Time>>{ fn next_move_instruction(&self)->Option<TimedInstruction<InternalInstruction,Time>>{
self.move_state.next_move_instruction(&self.style.strafe,self.time) self.move_state.next_move_instruction(self.style.strafe.as_ref())
} }
fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){ fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3,time:Time){
self.move_state.cull_velocity(velocity,&mut self.body,&mut self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); self.move_state.cull_velocity(velocity,&mut self.body,&mut self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state,time);
} }
fn set_move_state(&mut self,data:&PhysicsData,move_state:MoveState){ fn set_move_state(&mut self,data:&PhysicsData,move_state:MoveState){
self.move_state.set_move_state(move_state,&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); self.move_state.set_move_state(move_state,&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state);
@@ -1262,7 +1285,7 @@ fn recalculate_touching(
//I would have preferred while let Some(contact)=contacts.pop() //I would have preferred while let Some(contact)=contacts.pop()
//but there is no such method //but there is no such method
while let Some((&convex_mesh_id,_face_id))=touching.contacts.iter().next(){ while let Some((&convex_mesh_id,_face_id))=touching.contacts.iter().next(){
collision_end_contact(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,models.contact_attr(convex_mesh_id.model_id),&convex_mesh_id) collision_end_contact(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,models.contact_attr(convex_mesh_id.model_id),&convex_mesh_id,time)
} }
while let Some(&convex_mesh_id)=touching.intersects.iter().next(){ while let Some(&convex_mesh_id)=touching.intersects.iter().next(){
collision_end_intersect(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,mode,run,models.intersect_attr(convex_mesh_id.model_id),&convex_mesh_id,time); collision_end_intersect(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,mode,run,models.intersect_attr(convex_mesh_id.model_id),&convex_mesh_id,time);
@@ -1598,7 +1621,7 @@ fn collision_start_contact(
gameplay_attributes::SetTrajectory::TargetPointTime{..}=>todo!(), gameplay_attributes::SetTrajectory::TargetPointTime{..}=>todo!(),
gameplay_attributes::SetTrajectory::TargetPointSpeed{..}=>todo!(), gameplay_attributes::SetTrajectory::TargetPointSpeed{..}=>todo!(),
&gameplay_attributes::SetTrajectory::Velocity(velocity)=>{ &gameplay_attributes::SetTrajectory::Velocity(velocity)=>{
move_state.cull_velocity(velocity,body,touching,models,hitbox_mesh,style,camera,input_state); move_state.cull_velocity(velocity,body,touching,models,hitbox_mesh,style,camera,input_state,time);
}, },
gameplay_attributes::SetTrajectory::DotVelocity{..}=>todo!(), gameplay_attributes::SetTrajectory::DotVelocity{..}=>todo!(),
} }
@@ -1628,7 +1651,7 @@ fn collision_start_contact(
}else{ }else{
let jump_dir=walk_state.jump_direction.direction(models,hitbox_mesh,&walk_state.contact); let jump_dir=walk_state.jump_direction.direction(models,hitbox_mesh,&walk_state.contact);
let jumped_velocity=jump_settings.jumped_velocity(style,jump_dir,body.velocity,attr.general.booster.as_ref()); let jumped_velocity=jump_settings.jumped_velocity(style,jump_dir,body.velocity,attr.general.booster.as_ref());
move_state.cull_velocity(jumped_velocity,body,touching,models,hitbox_mesh,style,camera,input_state); move_state.cull_velocity(jumped_velocity,body,touching,models,hitbox_mesh,style,camera,input_state,time);
} }
} }
} }
@@ -1658,7 +1681,7 @@ fn collision_start_intersect(
touching.insert_intersect(intersect); touching.insert_intersect(intersect);
//insta booster! //insta booster!
if let Some(booster)=&attr.general.booster{ if let Some(booster)=&attr.general.booster{
move_state.cull_velocity(booster.boost(body.velocity),body,touching,models,hitbox_mesh,style,camera,input_state); move_state.cull_velocity(booster.boost(body.velocity),body,touching,models,hitbox_mesh,style,camera,input_state,time);
} }
if let Some(mode)=mode{ if let Some(mode)=mode{
let zone=mode.get_zone(intersect.convex_mesh_id.model_id.into()); let zone=mode.get_zone(intersect.convex_mesh_id.model_id.into());
@@ -1692,6 +1715,7 @@ fn collision_end_contact(
input_state:&InputState, input_state:&InputState,
_attr:&gameplay_attributes::ContactAttributes, _attr:&gameplay_attributes::ContactAttributes,
convex_mesh_id:&ConvexMeshId<ContactModelId>, convex_mesh_id:&ConvexMeshId<ContactModelId>,
time:Time,
){ ){
touching.remove_contact(convex_mesh_id);//remove contact before calling contact_constrain_acceleration touching.remove_contact(convex_mesh_id);//remove contact before calling contact_constrain_acceleration
//check ground //check ground
@@ -1702,7 +1726,7 @@ fn collision_end_contact(
// This does not check the face! Is that a bad thing? It should be // This does not check the face! Is that a bad thing? It should be
// impossible to stop touching a different face than you started touching... // impossible to stop touching a different face than you started touching...
Some(walk_state)=>if &walk_state.contact.convex_mesh_id==convex_mesh_id{ Some(walk_state)=>if &walk_state.contact.convex_mesh_id==convex_mesh_id{
move_state.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state); move_state.set_move_state(MoveState::air(time,style.strafe.as_ref()),body,touching,models,hitbox_mesh,style,camera,input_state);
}else{ }else{
// stopped touching something else while walking // stopped touching something else while walking
move_state.apply_enum_and_input_and_body(body,touching,models,hitbox_mesh,style,camera,input_state); move_state.apply_enum_and_input_and_body(body,touching,models,hitbox_mesh,style,camera,input_state);
@@ -1781,7 +1805,8 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
Collision::Contact(contact)=>collision_end_contact( Collision::Contact(contact)=>collision_end_contact(
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
data.models.contact_attr(contact.convex_mesh_id.model_id), data.models.contact_attr(contact.convex_mesh_id.model_id),
&contact.convex_mesh_id &contact.convex_mesh_id,
state.time,
), ),
Collision::Intersect(intersect)=>collision_end_intersect( Collision::Intersect(intersect)=>collision_end_intersect(
&mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state,
@@ -1793,6 +1818,11 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
), ),
}, },
InternalInstruction::StrafeTick=>{ InternalInstruction::StrafeTick=>{
let strafe_tick_state=match &mut state.move_state{
MoveState::Air(strafe_tick_state)=>strafe_tick_state,
_=>panic!("StrafeTick fired for non-air MoveState"),
};
strafe_tick_state.tick_number+=1;
//TODO make this less huge //TODO make this less huge
if let Some(strafe_settings)=&state.style.strafe{ if let Some(strafe_settings)=&state.style.strafe{
let controls=state.input_state.controls; let controls=state.input_state.controls;
@@ -1804,7 +1834,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){ if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
//this is wrong but will work ig //this is wrong but will work ig
//need to note which push planes activate in push solve and keep those //need to note which push planes activate in push solve and keep those
state.cull_velocity(data,ticked_velocity); state.cull_velocity(data,ticked_velocity,state.time);
} }
} }
} }
@@ -1812,7 +1842,7 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
} }
InternalInstruction::ReachWalkTargetVelocity=>{ InternalInstruction::ReachWalkTargetVelocity=>{
match &mut state.move_state{ match &mut state.move_state{
MoveState::Air MoveState::Air(_)
|MoveState::Water |MoveState::Water
|MoveState::Fly |MoveState::Fly
=>println!("ReachWalkTargetVelocity fired for non-walking MoveState"), =>println!("ReachWalkTargetVelocity fired for non-walking MoveState"),
@@ -1854,7 +1884,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|MoveState::Water |MoveState::Water
|MoveState::Walk(_) |MoveState::Walk(_)
|MoveState::Ladder(_)=>true, |MoveState::Ladder(_)=>true,
MoveState::Air=>false, MoveState::Air(_)=>false,
} }
}, },
//the body must be updated unconditionally //the body must be updated unconditionally
@@ -1888,7 +1918,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact); let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact);
let booster_option=data.models.contact_attr(walk_state.contact.convex_mesh_id.model_id).general.booster.as_ref(); let booster_option=data.models.contact_attr(walk_state.contact.convex_mesh_id.model_id).general.booster.as_ref();
let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,booster_option); let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,booster_option);
state.cull_velocity(data,jumped_velocity); state.cull_velocity(data,jumped_velocity,state.time);
} }
} }
b_refresh_walk_target=false; b_refresh_walk_target=false;
@@ -1918,7 +1948,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
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_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()); set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,vec3::zero());
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::air(state.time,state.style.strafe.as_ref()));
b_refresh_walk_target=false; b_refresh_walk_target=false;
} }
// Spawn does not necessarily imply reset // Spawn does not necessarily imply reset
@@ -1942,7 +1972,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
Instruction::Misc(MiscInstruction::PracticeFly)=>{ Instruction::Misc(MiscInstruction::PracticeFly)=>{
match &state.move_state{ match &state.move_state{
MoveState::Fly=>{ MoveState::Fly=>{
state.set_move_state(data,MoveState::Air); state.set_move_state(data,MoveState::air(state.time,state.style.strafe.as_ref()));
}, },
_=>{ _=>{
state.set_move_state(data,MoveState::Fly); state.set_move_state(data,MoveState::Fly);
@@ -1957,7 +1987,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
} }
if b_refresh_walk_target{ if b_refresh_walk_target{
state.apply_input_and_body(data); state.apply_input_and_body(data);
state.cull_velocity(data,state.body.velocity); state.cull_velocity(data,state.body.velocity,state.time);
//also check if accelerating away from surface //also check if accelerating away from surface
} }
} }

View File

@@ -2,7 +2,6 @@ const VALVE_SCALE:Planar64=Planar64::raw(1<<28);// 1/16
use crate::integer::{int,vec3::int as int3,AbsoluteTime,Ratio64,Planar64,Planar64Vec3}; use crate::integer::{int,vec3::int as int3,AbsoluteTime,Ratio64,Planar64,Planar64Vec3};
use crate::controls_bitflag::Controls; use crate::controls_bitflag::Controls;
use crate::physics::Time as PhysicsTime;
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct StyleModifiers{ pub struct StyleModifiers{
@@ -273,9 +272,6 @@ impl StrafeSettings{
false=>None, false=>None,
} }
} }
pub fn next_tick(&self,time:PhysicsTime)->PhysicsTime{
PhysicsTime::from_nanos(self.tick_rate.rhs_div_int(self.tick_rate.mul_int(time.nanos())+1))
}
pub const fn activates(&self,controls:Controls)->bool{ pub const fn activates(&self,controls:Controls)->bool{
self.enable.activates(controls) self.enable.activates(controls)
} }