This commit is contained in:
Quaternions 2024-02-01 22:14:58 -08:00
parent 35a0d0d655
commit 9f2c3cd242

View File

@ -1,9 +1,17 @@
use crate::model_physics::{PhysicsMesh,TransformedMesh,MeshQuery};
use std::collections::HashMap;
use std::collections::HashSet;
use crate::model_physics::{self,PhysicsMesh,TransformedMesh,MeshQuery};
use strafesnet_common::bvh;
use strafesnet_common::aabb;
use strafesnet_common::game_mechanics::{self,StyleModifiers};
use strafesnet_common::gameplay_attributes;
use strafesnet_common::gameplay_modes;
use strafesnet_common::map;
use strafesnet_common::model;
use strafesnet_common::gameplay_style::{self,StyleModifiers,StrafeSettings};
use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction};
use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2};
use strafesnet_common::model::ModelId;
#[derive(Debug)]
pub enum PhysicsInstruction {
@ -153,18 +161,16 @@ struct PhysicsModels{
//attributes can be split into contacting and intersecting (this also saves a bit of memory)
//can go even further and deduplicate General attributes separately, reconstructing it when queried
attributes:Vec<PhysicsCollisionAttributes>,
model_id_from_wormhole_id:std::collections::HashMap::<u32,usize>,
}
impl PhysicsModels{
fn clear(&mut self){
self.meshes.clear();
self.models.clear();
self.attributes.clear();
self.model_id_from_wormhole_id.clear();
}
//TODO: "statically" verify the maps don't refer to any nonexistant data, if they do delete the references.
//then I can make these getter functions unchecked.
fn mesh(&self,model_id:usize)->TransformedMesh{
fn mesh(&self,model_id:ModelId)->TransformedMesh{
TransformedMesh::new(
&self.meshes[self.models[model_id].mesh_id],
&self.models[model_id].transform,
@ -172,24 +178,21 @@ impl PhysicsModels{
self.models[model_id].transform_det,
)
}
fn model(&self,model_id:usize)->&PhysicsModel{
fn model(&self,model_id:ModelId)->&PhysicsModel{
&self.models[model_id]
}
fn attr(&self,model_id:usize)->&PhysicsCollisionAttributes{
fn attr(&self,model_id:ModelId)->&PhysicsCollisionAttributes{
&self.attributes[self.models[model_id].attr_id]
}
fn get_wormhole_model(&self,wormhole_id:u32)->Option<&PhysicsModel>{
self.models.get(*self.model_id_from_wormhole_id.get(&wormhole_id)?)
}
fn push_mesh(&mut self,mesh:PhysicsMesh){
self.meshes.push(mesh);
}
fn push_model(&mut self,model:PhysicsModel)->usize{
fn push_model(&mut self,model:PhysicsModel)->ModelId{
let model_id=self.models.len();
self.models.push(model);
model_id
}
fn push_attr(&mut self,attr:PhysicsCollisionAttributes)->usize{
fn push_attr(&mut self,attr:PhysicsCollisionAttributes)->PhysicsAttributeId{
let attr_id=self.attributes.len();
self.attributes.push(attr);
attr_id
@ -257,17 +260,17 @@ impl std::default::Default for PhysicsCamera{
}
}
pub struct GameMechanicsState{
stage_id:u32,
jump_counts:std::collections::HashMap<usize,u32>,//model_id -> jump count
next_ordered_checkpoint_id:u32,//which OrderedCheckpoint model_id you must pass next (if 0 you haven't passed OrderedCheckpoint0)
unordered_checkpoints:std::collections::HashSet<usize>,//hashset of UnorderedCheckpoint model ids
pub struct ModeState{
stage_id:gameplay_modes::StageId,
jump_counts:HashMap<ModelId,u32>,//model_id -> jump count
next_ordered_checkpoint_id:gameplay_modes::CheckpointId,//which OrderedCheckpoint model_id you must pass next (if 0 you haven't passed OrderedCheckpoint0)
unordered_checkpoints:HashSet<ModelId>,
}
impl std::default::Default for GameMechanicsState{
impl std::default::Default for ModeState{
fn default()->Self{
Self{
stage_id:0,
next_ordered_checkpoint_id:0,
stage_id:gameplay_modes::StageId::id(0),
next_ordered_checkpoint_id:gameplay_modes::CheckpointId::id(0),
unordered_checkpoints:std::collections::HashSet::new(),
jump_counts:std::collections::HashMap::new(),
}
@ -280,7 +283,7 @@ trait HitboxMeshPresets{
fn roblox()->Self;
fn source()->Self;
}
impl HitboxMeshPresets for game_mechanics::Hitbox{
impl HitboxMeshPresets for gameplay_style::Hitbox{
fn roblox()->Self{
Self::from_mesh_scale(PhysicsMesh::from(&crate::primitives::unit_cylinder()),Planar64Vec3::int(2,5,2)/2)
}
@ -309,12 +312,7 @@ impl StyleHelper for StyleModifiers{
fn allow_strafe(&self,controls:u32)->bool{
//disable strafing according to strafe settings
match &self.strafe{
Some(StrafeSettings{enable:EnableStrafe::Always,air_accel_limit:_,tick_rate:_})=>true,
&Some(StrafeSettings{enable:EnableStrafe::MaskAny(mask),air_accel_limit:_,tick_rate:_})=>mask&controls!=0,
&Some(StrafeSettings{enable:EnableStrafe::MaskAll(mask),air_accel_limit:_,tick_rate:_})=>mask&controls==mask,
None=>false,
}
self.strafe.is_some_and(|s|s.mask(controls))
}
fn get_control_dir(&self,controls:u32)->Planar64Vec3{
@ -429,21 +427,18 @@ pub struct PhysicsState{
time:Time,
body:Body,
world:WorldState,//currently there is only one state the world can be in
game:GameMechanicsState,
style:StyleModifiers,
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,
pub next_mouse:MouseState,//Where is the mouse headed next
controls:u32,
controls:u32,//TODO this should be a struct
move_state:MoveState,
models:PhysicsModels,
bvh:bvh::BvhNode,
modes:Modes,
//the spawn point is where you spawn when you load into the map.
//This is not the same as Reset which teleports you to Spawn0
spawn_point:Planar64Vec3,
//does not belong here
//bvh:bvh::BvhNode,
//models:PhysicsModels,
//modes:gameplay_modes::Modes,
}
#[derive(Clone,Default)]
pub struct PhysicsOutputState{
@ -460,38 +455,53 @@ impl PhysicsOutputState{
#[derive(Clone,Hash,Eq,PartialEq)]
enum PhysicsCollisionAttributes{
Contact{//track whether you are contacting the object
contacting:crate::model::ContactingAttributes,
general:crate::model::GameMechanicAttributes,
contacting:gameplay_attributes::ContactingAttributes,
general:gameplay_attributes::GeneralAttributes,
},
Intersect{//track whether you are intersecting the object
intersecting:crate::model::IntersectingAttributes,
general:crate::model::GameMechanicAttributes,
intersecting:gameplay_attributes::IntersectingAttributes,
general:gameplay_attributes::GeneralAttributes,
},
}
struct NonPhysicsError;
impl TryFrom<&crate::model::CollisionAttributes> for PhysicsCollisionAttributes{
impl TryFrom<&gameplay_attributes::CollisionAttributes> for PhysicsCollisionAttributes{
type Error=NonPhysicsError;
fn try_from(value:&crate::model::CollisionAttributes)->Result<Self,Self::Error>{
fn try_from(value:&gameplay_attributes::CollisionAttributes)->Result<Self,Self::Error>{
match value{
crate::model::CollisionAttributes::Decoration=>Err(NonPhysicsError),
crate::model::CollisionAttributes::Contact{contacting,general}=>Ok(Self::Contact{contacting:contacting.clone(),general:general.clone()}),
crate::model::CollisionAttributes::Intersect{intersecting,general}=>Ok(Self::Intersect{intersecting:intersecting.clone(),general:general.clone()}),
gameplay_attributes::CollisionAttributes::Decoration=>Err(NonPhysicsError),
gameplay_attributes::CollisionAttributes::Contact{contacting,general}=>Ok(Self::Contact{contacting:contacting.clone(),general:general.clone()}),
gameplay_attributes::CollisionAttributes::Intersect{intersecting,general}=>Ok(Self::Intersect{intersecting:intersecting.clone(),general:general.clone()}),
}
}
}
struct GeneralAttributesId(u32);
struct ContactAttributesId(u32);
struct IntersectAttributesId(u32);
enum PhysicsAttributesId{
Contact(ContactAttributesId),
Intersect(IntersectAttributesId)
}
//unique physics meshes indexed by this
struct MeshId{
model_id:ModelId,
group_id:GroupId,
}
pub struct PhysicsModel{
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
//in this iteration, all it needs is extents.
mesh_id:usize,
attr_id:usize,
mesh_id:MeshId,
//put these up on the Model (data normalization)
general_attributes:GeneralAttributesId,
collision_attributes:PhysicsAttributesId,
transform:integer::Planar64Affine3,
normal_transform:integer::Planar64Mat3,
transform_det:Planar64,
}
impl PhysicsModel{
pub fn new(mesh_id:usize,attr_id:usize,transform:integer::Planar64Affine3)->Self{
pub fn new(mesh_id:MeshId,attr_id:PhysicsAttributesId,transform:integer::Planar64Affine3)->Self{
Self{
mesh_id,
attr_id,
@ -504,12 +514,12 @@ impl PhysicsModel{
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
struct ContactCollision{
face_id:crate::model_physics::MinkowskiFace,
model_id:usize,//using id to avoid lifetimes
face_id:model_physics::MinkowskiFace,
model_id:ModelId,//using id to avoid lifetimes
}
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
struct IntersectCollision{
model_id:usize,
model_id:ModelId,
}
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
enum Collision{
@ -517,13 +527,13 @@ enum Collision{
Intersect(IntersectCollision),
}
impl Collision{
fn model_id(&self)->usize{
fn model_id(&self)->ModelId{
match self{
&Collision::Contact(ContactCollision{model_id,face_id:_})
|&Collision::Intersect(IntersectCollision{model_id})=>model_id,
}
}
fn face_id(&self)->Option<crate::model_physics::MinkowskiFace>{
fn face_id(&self)->Option<model_physics::MinkowskiFace>{
match self{
&Collision::Contact(ContactCollision{model_id:_,face_id})=>Some(face_id),
&Collision::Intersect(IntersectCollision{model_id:_})=>None,
@ -616,7 +626,7 @@ impl TouchingState{
PhysicsCollisionAttributes::Contact{contacting,general}=>{
let normal=contact_normal(models,&style_mesh,contact);
match &contacting.contact_behaviour{
Some(crate::model::ContactingBehaviour::Ladder(_))=>{
Some(model::ContactingBehaviour::Ladder(_))=>{
//ladder walkstate
let mut target_velocity=style.get_ladder_target_velocity(camera,controls,next_mouse,time,&normal);
self.constrain_velocity(models,&style_mesh,&mut target_velocity);
@ -651,7 +661,7 @@ impl TouchingState{
for contact in &self.contacts{
//detect face slide off
let model_mesh=models.mesh(contact.model_id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(face,time)|{
TimedInstruction{
time,
@ -664,7 +674,7 @@ impl TouchingState{
for intersect in &self.intersects{
//detect model collision in reverse
let model_mesh=models.mesh(intersect.model_id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(face,time)|{
TimedInstruction{
time,
@ -784,7 +794,7 @@ impl Default for PhysicsState{
next_mouse:MouseState::default(),
controls:0,
world:WorldState{},
game:GameMechanicsState::default(),
mode_state:ModeState::default(),
modes:Modes::default(),
}
}
@ -807,7 +817,7 @@ impl PhysicsState {
}
pub fn spawn(&mut self,spawn_point:Planar64Vec3){
self.game.stage_id=0;
self.mode_state.stage_id=0;
self.spawn_point=spawn_point;
self.process_instruction(instruction::TimedInstruction{
time:self.time,
@ -815,7 +825,7 @@ impl PhysicsState {
});
}
pub fn generate_models(&mut self,indexed_models:&crate::model::IndexedModelInstances){
pub fn generate_models(&mut self,indexed_models:&map::Map){
let mut starts=Vec::new();
let mut spawns=Vec::new();
let mut attr_hash=std::collections::HashMap::new();
@ -833,14 +843,7 @@ impl PhysicsState {
};
let model_physics=PhysicsModel::new(mesh_id,attr_id,model_instance.transform);
make_mesh=true;
let model_id=self.models.push_model(model_physics);
for attr in &model_instance.temp_indexing{
match attr{
crate::model::TempIndexedAttributes::Start(s)=>starts.push((model_id,s.clone())),
crate::model::TempIndexedAttributes::Spawn(s)=>spawns.push((model_id,s.clone())),
crate::model::TempIndexedAttributes::Wormhole(s)=>{self.models.model_id_from_wormhole_id.insert(s.wormhole_id,model_id);},
}
}
self.models.push_model(model_physics);
}
}
if make_mesh{
@ -848,32 +851,6 @@ impl PhysicsState {
}
}
self.bvh=bvh::generate_bvh(self.models.aabb_list());
//I don't wanna write structs for temporary structures
//this code builds ModeDescriptions from the unsorted lists at the top of the function
starts.sort_by_key(|tup|tup.1.mode_id);
let mut mode_id_from_map_mode_id=std::collections::HashMap::new();
let mut modedatas:Vec<(usize,Vec<(u32,usize)>,u32)>=starts.into_iter().enumerate().map(|(i,(model_id,s))|{
mode_id_from_map_mode_id.insert(s.mode_id,i);
(model_id,Vec::new(),s.mode_id)
}).collect();
for (model_id,s) in spawns{
if let Some(mode_id)=mode_id_from_map_mode_id.get(&s.mode_id){
if let Some(modedata)=modedatas.get_mut(*mode_id){
modedata.1.push((s.stage_id,model_id));
}
}
}
for mut tup in modedatas.into_iter(){
tup.1.sort_by_key(|tup|tup.0);
let mut eshmep1=std::collections::HashMap::new();
let mut eshmep2=std::collections::HashMap::new();
self.modes.insert(tup.2,crate::model::ModeDescription{
start:tup.0,
spawns:tup.1.into_iter().enumerate().map(|(i,tup)|{eshmep1.insert(tup.0,i);tup.1}).collect(),
spawn_from_stage_id:eshmep1,
ordered_checkpoint_from_checkpoint_id:eshmep2,
});
}
println!("Physics Objects: {}",self.models.models.len());
}
@ -903,7 +880,7 @@ impl PhysicsState {
fn next_strafe_instruction(&self)->Option<TimedInstruction<PhysicsInstruction>>{
self.style.strafe.as_ref().map(|strafe|{
TimedInstruction{
time:Time::from_nanos(strafe.tick_rate.rhs_div_int(strafe.tick_rate.mul_int(self.time.nanos())+1)),
time:strafe.next_tick(self.time),
//only poll the physics if there is a before and after mouse event
instruction:PhysicsInstruction::StrafeTick
}
@ -1003,7 +980,7 @@ impl instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState{
self.bvh.the_tester(&aabb,&mut |id|{
//no checks are needed because of the time limits.
let model_mesh=self.models.mesh(id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&style_mesh);
collector.collect(minkowski.predict_collision_in(&relative_body,collector.time())
//temp (?) code to avoid collision loops
.map_or(None,|(face,time)|if time==self.time{None}else{Some((face,time))})
@ -1035,7 +1012,7 @@ fn jumped_velocity(models:&PhysicsModels,style:&StyleModifiers,walk_state:&WalkS
fn contact_normal(models:&PhysicsModels,style_mesh:&TransformedMesh,contact:&ContactCollision)->Planar64Vec3{
let model_mesh=models.mesh(contact.model_id);
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,style_mesh);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,style_mesh);
minkowski.face_nd(contact.face_id).0
}
@ -1095,31 +1072,30 @@ fn teleport(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,sty
set_acceleration(body,touching,models,&style.mesh(),style.gravity);
MoveState::Air
}
fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,mode:&crate::model::ModeDescription,models:&PhysicsModels,stage_id:u32)->Option<MoveState>{
let model=models.model(*mode.get_spawn_model_id(stage_id)? as usize);
fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,mode:&gameplay_modes::Mode,models:&PhysicsModels,stage_id:StageId)->Option<MoveState>{
let model=models.model(mode.get_spawn_model_id(stage_id)?);
let point=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
Some(teleport(body,touching,models,style,point))
}
fn run_teleport_behaviour(teleport_behaviour:&Option<crate::model::TeleportBehaviour>,game:&mut GameMechanicsState,models:&PhysicsModels,modes:&Modes,style:&StyleModifiers,touching:&mut TouchingState,body:&mut Body,model_id:usize)->Option<MoveState>{
fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,touching:&mut TouchingState,body:&mut Body,model_id:ModelId)->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
match teleport_behaviour{
Some(crate::model::TeleportBehaviour::StageElement(stage_element))=>{
mode.get_element(model_id).map(|stage_element|{
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;
}
match &stage_element.behaviour{
crate::model::StageElementBehaviour::SpawnAt=>None,
crate::model::StageElementBehaviour::Trigger
|crate::model::StageElementBehaviour::Teleport=>{
model::StageElementBehaviour::SpawnAt=>None,
model::StageElementBehaviour::Trigger
|model::StageElementBehaviour::Teleport=>{
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
teleport_to_spawn(body,touching,style,modes.get_mode(stage_element.mode_id)?,models,game.stage_id)
},
crate::model::StageElementBehaviour::Platform=>None,
&crate::model::StageElementBehaviour::Checkpoint=>{
model::StageElementBehaviour::Platform=>None,
&model::StageElementBehaviour::Checkpoint=>{
// let mode=modes.get_mode(stage_element.mode_id)?;
// if mode.ordered_checkpoint_id.map_or(true,|id|id<game.next_ordered_checkpoint_id)
// &&mode.unordered_checkpoint_count<=game.unordered_checkpoints.len() as u32{
@ -1130,7 +1106,7 @@ fn run_teleport_behaviour(teleport_behaviour:&Option<crate::model::TeleportBehav
// teleport_to_spawn(body,touching,style,modes.get_mode(stage_element.mode_id)?,models,game.stage_id)
// }
},
&crate::model::StageElementBehaviour::Ordered{checkpoint_id}=>{
&model::StageElementBehaviour::Ordered{checkpoint_id}=>{
if checkpoint_id<game.next_ordered_checkpoint_id{
//if you hit a checkpoint you already hit, nothing happens
None
@ -1144,26 +1120,27 @@ fn run_teleport_behaviour(teleport_behaviour:&Option<crate::model::TeleportBehav
teleport_to_spawn(body,touching,style,modes.get_mode(stage_element.mode_id)?,models,game.stage_id)
}
},
crate::model::StageElementBehaviour::Unordered=>{
model::StageElementBehaviour::Unordered=>{
//count model id in accumulated unordered checkpoints
game.unordered_checkpoints.insert(model_id);
None
},
&crate::model::StageElementBehaviour::JumpLimit(jump_limit)=>{
&model::StageElementBehaviour::JumpLimit(jump_limit)=>{
//let count=game.jump_counts.get(&model.id);
//TODO
None
},
}
},
Some(crate::model::TeleportBehaviour::Wormhole(wormhole))=>{
}).or_else(||
match wormhole{
Some(gameplay_attributes::Wormhole{destination_model})=>{
let origin_model=models.model(model_id);
let destination_model=models.get_wormhole_model(wormhole.destination_model_id)?;
let destination_model=models.get_wormhole_model(destination_model)?;
//ignore the transform for now
Some(teleport(body,touching,models,style,body.position-origin_model.transform.translation+destination_model.transform.translation))
}
None=>None,
}
})
}
impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
@ -1193,14 +1170,14 @@ impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
let mut v=self.body.velocity;
let normal=contact_normal(&self.models,&style_mesh,contact);
match &contacting.contact_behaviour{
Some(crate::model::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
Some(crate::model::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
&Some(crate::model::ContactingBehaviour::Elastic(elasticity))=>{
Some(gameplay_attributes::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
//velocity and normal are facing opposite directions so this is inherently negative.
let d=normal.dot(v)*(Planar64::ONE+Planar64::raw(elasticity as i64+1));
v+=normal*(d/normal.dot(normal));
},
Some(crate::model::ContactingBehaviour::Ladder(contacting_ladder))=>{
Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>{
if contacting_ladder.sticky{
//kill v
//actually you could do this with a booster attribute :thinking:
@ -1227,16 +1204,16 @@ impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
//check ground
self.touching.insert(c);
//I love making functions with 10 arguments to dodge the borrow checker
run_teleport_behaviour(&general.teleport_behaviour,&mut self.game,&self.models,&self.modes,&self.style,&mut self.touching,&mut self.body,model_id);
run_teleport_behaviour(&general.teleport_behaviour,&mut self.mode_state,&self.models,&self.modes,&self.style,&mut self.touching,&mut self.body,model_id);
//flatten v
self.touching.constrain_velocity(&self.models,&style_mesh,&mut v);
match &general.booster{
Some(booster)=>{
//DELETE THIS when boosters get converted to height machines
match booster{
&crate::model::GameMechanicBooster::Affine(transform)=>v=transform.transform_point3(v),
&crate::model::GameMechanicBooster::Velocity(velocity)=>v+=velocity,
&crate::model::GameMechanicBooster::Energy{direction: _,energy: _}=>todo!(),
&gameplay_attributes::Booster::Affine(transform)=>v=transform.transform_point3(v),
&gameplay_attributes::Booster::Velocity(velocity)=>v+=velocity,
&gameplay_attributes::Booster::Energy{direction: _,energy: _}=>todo!(),
}
},
None=>(),
@ -1250,12 +1227,12 @@ impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
match &general.trajectory{
Some(trajectory)=>{
match trajectory{
crate::model::GameMechanicSetTrajectory::AirTime(_) => todo!(),
crate::model::GameMechanicSetTrajectory::Height(_) => todo!(),
crate::model::GameMechanicSetTrajectory::TargetPointTime { target_point: _, time: _ } => todo!(),
crate::model::GameMechanicSetTrajectory::TargetPointSpeed { target_point: _, speed: _, trajectory_choice: _ } => todo!(),
&crate::model::GameMechanicSetTrajectory::Velocity(velocity)=>v=velocity,
crate::model::GameMechanicSetTrajectory::DotVelocity { direction: _, dot: _ } => todo!(),
gameplay_attributes::SetTrajectory::AirTime(_) => todo!(),
gameplay_attributes::SetTrajectory::Height(_) => todo!(),
gameplay_attributes::SetTrajectory::TargetPointTime { target_point: _, time: _ } => todo!(),
gameplay_attributes::SetTrajectory::TargetPointSpeed { target_point: _, speed: _, trajectory_choice: _ } => todo!(),
&gameplay_attributes::SetTrajectory::Velocity(velocity)=>v=velocity,
gameplay_attributes::SetTrajectory::DotVelocity { direction: _, dot: _ } => todo!(),
}
},
None=>(),
@ -1271,7 +1248,7 @@ impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
(PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
self.touching.insert(c);
run_teleport_behaviour(&general.teleport_behaviour,&mut self.game,&self.models,&self.modes,&self.style,&mut self.touching,&mut self.body,model_id);
run_teleport_behaviour(&general.teleport_behaviour,&mut self.mode_state,&self.models,&self.modes,&self.style,&mut self.touching,&mut self.body,model_id);
},
_=>panic!("invalid pair"),
}
@ -1378,17 +1355,17 @@ impl instruction::InstructionConsumer<PhysicsInstruction> for PhysicsState {
#[allow(dead_code)]
fn test_collision_axis_aligned(relative_body:Body,expected_collision_time:Option<Time>){
let h0=Hitbox::from_mesh_scale(PhysicsMesh::from(&crate::primitives::unit_cube()),Planar64Vec3::int(5,1,5)/2);
let h1=Hitbox::roblox();
let h0=gameplay_style::Hitbox::from_mesh_scale(PhysicsMesh::from(&crate::primitives::unit_cube()),Planar64Vec3::int(5,1,5)/2);
let h1=gameplay_style::Hitbox::roblox();
let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh();
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh);
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=Hitbox::new(PhysicsMesh::from(&crate::primitives::unit_cube()),
let h0=gameplay_style::Hitbox::new(PhysicsMesh::from(&crate::primitives::unit_cube()),
integer::Planar64Affine3::new(
integer::Planar64Mat3::from_cols(
Planar64Vec3::int(5,0,1)/2,
@ -1398,10 +1375,10 @@ fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time
Planar64Vec3::ZERO,
)
);
let h1=Hitbox::roblox();
let h1=gameplay_style::Hitbox::roblox();
let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh();
let minkowski=crate::model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh);
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
}