work happening

This commit is contained in:
Quaternions 2024-02-08 19:10:59 -08:00
parent 64baa64dd3
commit 7403ec8bed

View File

@ -1,16 +1,16 @@
use std::collections::HashMap;
use std::collections::HashSet;
use crate::model_physics::{self,PhysicsMesh,PhysicsMeshTransform,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId};
use strafesnet_common::bvh;
use strafesnet_common::map;
use strafesnet_common::aabb;
use strafesnet_common::gameplay_modes;
use strafesnet_common::gameplay_modes::{self,StageId};
use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId};
use strafesnet_common::model::ModelId;
use strafesnet_common::gameplay_style::{self,StyleModifiers};
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
use gameplay::ModeState;
#[derive(Debug)]
pub enum PhysicsInstruction {
@ -182,19 +182,6 @@ impl PhysicsModels{
fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{
&self.attributes[&self.models[&model_id].attr_id]
}
fn push_mesh(&mut self,mesh:PhysicsMesh){
self.meshes.push(mesh);
}
fn push_model(&mut self,model:PhysicsModel)->PhysicsModelId{
let model_id=PhysicsModelId::new(self.models.len() as u32);
self.models.push(model);
model_id
}
fn push_attr(&mut self,attr:PhysicsCollisionAttributes)->PhysicsAttributesId{
let attr_id=PhysicsAttributesId::new(self.attributes.len() as u32);
self.attributes.push(attr);
attr_id
}
}
#[derive(Clone)]
@ -255,7 +242,8 @@ impl std::default::Default for PhysicsCamera{
}
}
}
mod gameplay{
use super::{gameplay_modes,HashSet,HashMap,ModelId};
pub struct ModeState{
mode_id:gameplay_modes::ModeId,
stage_id:gameplay_modes::StageId,
@ -263,17 +251,67 @@ pub struct ModeState{
unordered_checkpoints:HashSet<ModelId>,
jump_counts:HashMap<ModelId,u32>,//model_id -> jump count
}
impl ModeState{
pub const fn get_mode_id(&self)->gameplay_modes::ModeId{
self.mode_id
}
pub const fn get_stage_id(&self)->gameplay_modes::StageId{
self.stage_id
}
pub const fn get_next_ordered_checkpoint_id(&self)->gameplay_modes::CheckpointId{
self.next_ordered_checkpoint_id
}
pub const fn get_jump_count(&self,model_id:ModelId)->Option<u32>{
self.jump_counts.get(&model_id).copied()
}
pub const fn ordered_checkpoint_count(&self)->u32{
self.next_ordered_checkpoint_id.get()
}
pub const fn unordered_checkpoint_count(&self)->u32{
self.unordered_checkpoints.len() as u32
}
pub fn set_mode_id(&mut self,mode_id:gameplay_modes::ModeId){
self.clear();
self.mode_id=mode_id;
}
pub fn set_stage_id(&mut self,stage_id:gameplay_modes::StageId){
self.clear_checkpoints();
self.stage_id=stage_id;
}
pub const fn accumulate_ordered_checkpoint(&mut self,stage:&gameplay_modes::Stage,model_id:ModelId){
if stage.is_next_ordered_checkpoint(self.get_next_ordered_checkpoint_id(),model_id){
self.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::new(self.next_ordered_checkpoint_id.get()+1);
}
}
pub fn accumulate_unordered_checkpoint(&mut self,stage:&gameplay_modes::Stage,model_id:ModelId){
if stage.is_unordered_checkpoint(model_id){
self.unordered_checkpoints.insert(model_id);
}
}
pub fn clear(&mut self){
self.clear_jump_counts();
self.clear_checkpoints();
}
pub fn clear_jump_counts(&mut self){
self.jump_counts.clear();
}
pub fn clear_checkpoints(&mut self){
self.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::FIRST;
self.unordered_checkpoints.clear();
}
}
impl std::default::Default for ModeState{
fn default()->Self{
Self{
mode_id:gameplay_modes::ModeId::new(0),
stage_id:gameplay_modes::StageId::new(0),
next_ordered_checkpoint_id:gameplay_modes::CheckpointId::new(0),
mode_id:gameplay_modes::ModeId::MAIN,
stage_id:gameplay_modes::StageId::FIRST,
next_ordered_checkpoint_id:gameplay_modes::CheckpointId::FIRST,
unordered_checkpoints:HashSet::new(),
jump_counts:HashMap::new(),
}
}
}
}
struct WorldState{}
@ -297,7 +335,7 @@ impl HitboxMesh{
}
#[inline]
fn transformed_mesh(&self)->TransformedMesh{
TransformedMesh::new(&self.mesh.complete_mesh_view(),&self.transform)
TransformedMesh::new(self.mesh.complete_mesh_view(),&self.transform)
}
}
@ -792,22 +830,26 @@ pub struct PhysicsState{
time:Time,
body:Body,
world:WorldState,//currently there is only one state the world can be in
mode_state:ModeState,
style:StyleModifiers,//mode style with custom style updates applied
touching:TouchingState,
//camera must exist in state because wormholes modify the camera, also camera punch
camera:PhysicsCamera,
//input_state
pub next_mouse:MouseState,//Where is the mouse headed next
controls:u32,//TODO this should be a struct
//style
style:StyleModifiers,//mode style with custom style updates applied
//gameplay_state
mode_state:ModeState,
move_state:MoveState,
}
//random collection of contextual data that doesn't belong in PhysicsState
pub struct PhysicsData{
//permanent map data
bvh:bvh::BvhNode<ConvexMeshId>,
modes:gameplay_modes::Modes,
//transient map/environment data (open world may load/unload)
//transient map/environment data (open world loads/unloads parts of this data)
models:PhysicsModels,
//semi-transient data
modes:gameplay_modes::Modes,
//cached calculations
hitbox_mesh:HitboxMesh,
}
@ -927,7 +969,6 @@ impl instruction::InstructionEmitter<PhysicsInstruction> for PhysicsContext{
}
impl PhysicsContext{
pub fn spawn(&mut self){
self.state.mode_state.stage_id=gameplay_modes::StageId::new(0);
self.process_instruction(instruction::TimedInstruction{
time:self.state.time,
instruction: PhysicsInstruction::Input(PhysicsInputInstruction::Reset),
@ -975,33 +1016,33 @@ impl PhysicsContext{
//write hash lol
}
}
}
//TODO get rid of this trash
fn refresh_walk_target(&mut self)->Planar64Vec3{
match &mut self.state.move_state{
MoveState::Air|MoveState::Water=>self.state.touching.base_acceleration(&self.data.models,&self.state.style,&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time),
fn refresh_walk_target(s:&mut PhysicsState,data:&PhysicsData)->Planar64Vec3{
match &mut s.move_state{
MoveState::Air|MoveState::Water=>s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time),
MoveState::Walk(WalkState{state,contact,jump_direction:_})=>{
let n=contact_normal(&self.data.models,&self.data.hitbox_mesh,contact);
let gravity=self.state.touching.base_acceleration(&self.data.models,&self.state.style,&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time);
let n=contact_normal(&data.models,&data.hitbox_mesh,contact);
let gravity=s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time);
let mut a;
let mut v=self.state.style.get_walk_target_velocity(&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time,&n);
self.state.touching.constrain_velocity(&self.data.models,&self.data.hitbox_mesh,&mut v);
let mut v=s.style.get_walk_target_velocity(&s.camera,s.controls,&s.next_mouse,s.time,&n);
s.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
let normal_accel=-n.dot(gravity)/n.length();
(*state,a)=WalkEnum::with_target_velocity(&self.state.body,&self.state.style,v,&n,self.state.style.walk_speed,normal_accel);
(*state,a)=WalkEnum::with_target_velocity(&s.body,&s.style,v,&n,s.style.walk_speed,normal_accel);
a
},
MoveState::Ladder(WalkState{state,contact,jump_direction:_})=>{
let n=contact_normal(&self.data.models,&self.data.hitbox_mesh,contact);
let gravity=self.state.touching.base_acceleration(&self.data.models,&self.state.style,&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time);
let n=contact_normal(&data.models,&data.hitbox_mesh,contact);
let gravity=s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time);
let mut a;
let mut v=self.state.style.get_ladder_target_velocity(&self.state.camera,self.state.controls,&self.state.next_mouse,self.state.time,&n);
self.state.touching.constrain_velocity(&self.data.models,&self.data.hitbox_mesh,&mut v);
(*state,a)=WalkEnum::with_target_velocity(&self.state.body,&self.state.style,v,&n,self.state.style.ladder_speed,self.state.style.ladder_accel);
let mut v=s.style.get_ladder_target_velocity(&s.camera,s.controls,&s.next_mouse,s.time,&n);
s.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
(*state,a)=WalkEnum::with_target_velocity(&s.body,&s.style,v,&n,s.style.ladder_speed,s.style.ladder_accel);
a
},
}
}
}
fn literally_next_instruction_but_with_context(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<PhysicsInstruction>>{
//JUST POLLING!!! NO MUTATION
@ -1118,46 +1159,53 @@ fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModi
Some(teleport(body,touching,models,style,hitbox_mesh,point))
}
fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,touching:&mut TouchingState,body:&mut Body,convex_mesh_id:ConvexMeshId)->Option<MoveState>{
fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,mode_state:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,touching:&mut TouchingState,body:&mut Body,convex_mesh_id:ConvexMeshId)->Option<MoveState>{
//TODO: jump count and checkpoints are always reset on teleport.
//Map makers are expected to use tools to prevent
//multi-boosting on JumpLimit boosters such as spawning into a SetVelocity
if let Some(stage_element)=mode.get_element(convex_mesh_id.model_id.into()){
let stage=mode.get_stage(stage_element.stage_id)?;
if stage_element.force||game.stage_id<stage_element.stage_id{
//TODO: check if all checkpoints are complete up to destination stage id, otherwise set to checkpoint completion stage it
game.stage_id=stage_element.stage_id;
if let Some(stage)=mode.get_stage(stage_element.stage_id()){
if mode_state.get_stage_id()<stage_element.stage_id(){
//checkpoint check
//check if current stage is complete
if let Some(current_stage)=mode.get_stage(mode_state.get_stage_id()){
if !current_stage.is_complete(mode_state.ordered_checkpoint_count(),mode_state.unordered_checkpoint_count()){
//do the stage checkpoints have to be reset?
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,mode_state.get_stage_id());
}
match &stage_element.behaviour{
}
//check if all between stages have no checkpoints required to pass them
for stage_id in mode_state.get_stage_id().get()+1..stage_element.stage_id().get(){
let stage_id=StageId::new(stage_id);
//check if none of the between stages has checkpoints, if they do teleport back to that stage
if !mode.get_stage(stage_id)?.is_empty(){
mode_state.set_stage_id(stage_id);
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,stage_id);
}
}
//notably you do not get teleported for touching ordered checkpoints in the wrong order within the same stage.
mode_state.set_stage_id(stage_element.stage_id());
}else if stage_element.force(){
//forced stage_element will set the stage_id even if the stage has already been passed
mode_state.set_stage_id(stage_element.stage_id());
}
match stage_element.behaviour(){
gameplay_modes::StageElementBehaviour::SpawnAt=>(),
gameplay_modes::StageElementBehaviour::Trigger
|gameplay_modes::StageElementBehaviour::Teleport=>{
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,game.stage_id);
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,mode_state.get_stage_id());
},
gameplay_modes::StageElementBehaviour::Platform=>(),
&gameplay_modes::StageElementBehaviour::Checkpoint=>{
//checkpoint check
//TODO: need to check all stages
if stage.ordered_checkpoint_id.map_or(true,|id|id<game.next_ordered_checkpoint_id)
&&stage.unordered_checkpoint_count<=game.unordered_checkpoints.len() as u32{
//pass
}else{
//fail
return teleport_to_spawn(body,touching,style,hitbox_mesh,mode,models,game.stage_id);
}
gameplay_modes::StageElementBehaviour::Check=>(),//this is to run the checkpoint check behaviour without any other side effects
gameplay_modes::StageElementBehaviour::Checkpoint=>{
//each of these checks if the model is actually a valid respective checkpoint object
//accumulate sequential ordered checkpoints
mode_state.accumulate_ordered_checkpoint(&stage,convex_mesh_id.model_id.into());
//insert model id in accumulated unordered checkpoints
mode_state.accumulate_unordered_checkpoint(&stage,convex_mesh_id.model_id.into());
},
}
if let Some(next_checkpoint)=stage.ordered_checkpoints.get(&game.next_ordered_checkpoint_id){
if convex_mesh_id==next_checkpoint{
//if you hit the next number in a sequence of ordered checkpoints
//increment the current checkpoint id
game.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::new(game.next_ordered_checkpoint_id.get()+1);
}
}
if stage.unordered_checkpoints.contains(convex_mesh_id.model_id.into()){
//count model id in accumulated unordered checkpoints
game.unordered_checkpoints.insert(convex_mesh_id.model_id.into());
}
}
match wormhole{
@ -1230,7 +1278,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
//check ground
state.touching.insert(c);
//I love making functions with 10 arguments to dodge the borrow checker
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.get_mode_id()).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
//flatten v
state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
match &general.booster{
@ -1268,13 +1316,13 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
if calc_move||Planar64::ZERO<normal.dot(v){
(state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time);
}
let a=state.refresh_walk_target();
let a=refresh_walk_target(state,data);
set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a);
},
(PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
state.touching.insert(c);
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.get_mode_id()).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
},
_=>panic!("invalid pair"),
}
@ -1360,8 +1408,10 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
},
PhysicsInputInstruction::Reset => {
//it matters which of these runs first, but I have not thought it through yet as it doesn't matter yet
state.mode_state.clear();
state.mode_state.set_stage_id(gameplay_modes::StageId::FIRST);
let spawn_point={
let mode=data.modes.get_mode(state.mode_state.mode_id).unwrap();
let mode=data.modes.get_mode(state.mode_state.get_mode_id()).unwrap();
let stage=mode.get_stage(gameplay_modes::StageId::FIRST).unwrap();
data.models.model(stage.spawn().into()).transform.vertex.translation
};
@ -1373,7 +1423,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
PhysicsInputInstruction::Idle => {refresh_walk_target=false;},//literally idle!
}
if refresh_walk_target{
let a=state.refresh_walk_target();
let a=refresh_walk_target(state,data);
if set_acceleration_cull(&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,a){
(state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time);
}
@ -1382,7 +1432,9 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
}
}
#[allow(dead_code)]
#[cfg(test)]
mod test{
use super::*;
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
let h0=HitboxMesh::from_mesh_scale(PhysicsMesh::unit_cube(),Planar64Vec3::int(5,1,5)/2);
let h1=HitboxMesh::roblox();
@ -1392,7 +1444,6 @@ fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
}
#[allow(dead_code)]
fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time>){
let h0=HitboxMesh::new(PhysicsMesh::unit_cube(),
integer::Planar64Affine3::new(
@ -1411,7 +1462,6 @@ fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
}
#[allow(dead_code)]
fn test_collision(relative_body:Body,expected_collision_time:Option<Time>){
test_collision_axis_aligned(relative_body.clone(),expected_collision_time);
test_collision_rotated(relative_body,expected_collision_time);
@ -1596,3 +1646,4 @@ fn already_inside_hit_nothing(){
Time::ZERO
),None);
}
}