factor out immutable checkpoint_check logic

This commit is contained in:
Quaternions 2025-01-06 20:25:53 -08:00
parent 4f21985290
commit 1dc2556d85

View File

@ -1441,7 +1441,7 @@ enum TeleportToSpawnError{
NoModel,
}
fn teleport_to_spawn(
stage:&gameplay_modes::Stage,
spawn_model_id:ModelId,
move_state:&mut MoveState,
body:&mut Body,
touching:&mut TouchingState,
@ -1457,13 +1457,71 @@ fn teleport_to_spawn(
time:Time,
)->Result<(),TeleportToSpawnError>{
const EPSILON:Planar64=Planar64::raw((1<<32)/16);
let transform=models.get_model_transform(stage.spawn()).ok_or(TeleportToSpawnError::NoModel)?;
let transform=models.get_model_transform(spawn_model_id).ok_or(TeleportToSpawnError::NoModel)?;
//TODO: transform.vertex.matrix3.col(1)+transform.vertex.translation
let point=transform.vertex.transform_point3(vec3::Y).fix_1()+Planar64Vec3::new([Planar64::ZERO,style.hitbox.halfsize.y+EPSILON,Planar64::ZERO]);
teleport(point,move_state,body,touching,run,mode_state,Some(mode),models,hitbox_mesh,bvh,style,camera,input_state,time);
Ok(())
}
struct CheckpointCheckOutcome{
set_stage:Option<StageId>,
teleport_to_model:Option<ModelId>,
}
fn checkpoint_check(
mode_state:&ModeState,
stage_element:&gameplay_modes::StageElement,
mode:&gameplay_modes::Mode,
)->CheckpointCheckOutcome{
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 CheckpointCheckOutcome{
set_stage:None,
teleport_to_model:Some(current_stage.spawn()),
};
}
}
//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
match mode.get_stage(stage_id){
Some(stage)=>if !stage.is_empty(){
return CheckpointCheckOutcome{
set_stage:Some(stage_id),
teleport_to_model:Some(stage.spawn()),
};
},
//no such stage! set to last existing stage
None=>return CheckpointCheckOutcome{
set_stage:Some(StageId::new(stage_id.get()-1)),
teleport_to_model:None,
},
}
};
//notably you do not get teleported for touching ordered checkpoints in the wrong order within the same stage.
return CheckpointCheckOutcome{
set_stage:Some(stage_element.stage_id()),
teleport_to_model:None,
};
}else if stage_element.force(){
//forced stage_element will set the stage_id even if the stage has already been passed
return CheckpointCheckOutcome{
set_stage:Some(stage_element.stage_id()),
teleport_to_model:None,
};
}
CheckpointCheckOutcome{
set_stage:None,
teleport_to_model:None,
}
}
fn run_teleport_behaviour(
model_id:ModelId,
wormhole:Option<&gameplay_attributes::Wormhole>,
@ -1485,66 +1543,37 @@ fn run_teleport_behaviour(
//Map makers are expected to use tools to prevent
//multi-boosting on JumpLimit boosters such as spawning into a SetVelocity
if let Some(mode)=mode{
if let Some(stage_element)=mode.get_element(model_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?
let _=teleport_to_spawn(current_stage,move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
return;
}
if let Some(stage_element)=mode.get_element(model_id){
if let Some(stage)=mode.get_stage(stage_element.stage_id()){
let CheckpointCheckOutcome{set_stage,teleport_to_model}=checkpoint_check(mode_state,stage_element,mode);
if let Some(stage_id)=set_stage{
mode_state.set_stage_id(stage_id);
}
//check if all between stages have no checkpoints required to pass them
let mut loop_unbroken=true;
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
match mode.get_stage(stage_id){
Some(stage)=>if !stage.is_empty(){
mode_state.set_stage_id(stage_id);
let _=teleport_to_spawn(stage,move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
return;
},
None=>{
//no such stage! set to last existing stage and break loop
mode_state.set_stage_id(StageId::new(stage_id.get()-1));
loop_unbroken=false;
break;
},
}
};
//notably you do not get teleported for touching ordered checkpoints in the wrong order within the same stage.
if loop_unbroken{
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=>if let Some(mode_state_stage)=mode.get_stage(mode_state.get_stage_id()){
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
let _=teleport_to_spawn(mode_state_stage,move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
if let Some(model_id)=teleport_to_model{
let _=teleport_to_spawn(model_id,move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
return;
},
gameplay_modes::StageElementBehaviour::Platform=>(),
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,model_id);
//insert model id in accumulated unordered checkpoints
mode_state.accumulate_unordered_checkpoint(&stage,model_id);
},
}
match stage_element.behaviour(){
gameplay_modes::StageElementBehaviour::SpawnAt=>(),
gameplay_modes::StageElementBehaviour::Trigger
|gameplay_modes::StageElementBehaviour::Teleport=>if let Some(mode_state_stage)=mode.get_stage(mode_state.get_stage_id()){
//I guess this is correct behaviour when trying to teleport to a non-existent spawn but it's still weird
let _=teleport_to_spawn(mode_state_stage.spawn(),move_state,body,touching,run,mode_state,mode,models,hitbox_mesh,bvh,style,camera,input_state,time);
return;
},
gameplay_modes::StageElementBehaviour::Platform=>(),
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,model_id);
//insert model id in accumulated unordered checkpoints
mode_state.accumulate_unordered_checkpoint(&stage,model_id);
},
}
}
}
}
}
if let Some(&gameplay_attributes::Wormhole{destination_model})=wormhole{
if let (Some(origin),Some(destination))=(models.get_model_transform(model_id),models.get_model_transform(destination_model)){
let point=body.position-origin.vertex.translation+destination.vertex.translation;
@ -1914,7 +1943,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
if let Some(mode)=data.modes.get_mode(mode_id){
if let Some(stage)=mode.get_stage(stage_id){
let _=teleport_to_spawn(
stage,
stage.spawn(),
&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