diff --git a/src/gameplay_attributes.rs b/src/gameplay_attributes.rs
index 75e9024..92ee4dd 100644
--- a/src/gameplay_attributes.rs
+++ b/src/gameplay_attributes.rs
@@ -1,3 +1,4 @@
+use crate::model;
 use crate::integer::{Time,Planar64,Planar64Vec3};
 
 //you have this effect while in contact
@@ -77,7 +78,7 @@ pub struct Wormhole{
 	//destination does not need to be another wormhole
 	//this defines a one way portal to a destination model transform
 	//two of these can create a two way wormhole
-	pub destination_model_id:u32,
+	pub destination_model:model::ModelId,
 	//(position,angles)*=origin.transform.inverse()*destination.transform
 }
 //attributes listed in order of handling
@@ -132,6 +133,12 @@ impl IntersectingAttributes{
 	}
 }
 pub struct CollisionAttributesId(u32);
+impl CollisionAttributesId{
+	pub fn new(id:u32)->Self{
+		Self(id)
+	}
+}
+#[derive(Clone,Hash,Eq,PartialEq)]
 pub enum CollisionAttributes{
 	Decoration,//visual only
 	Contact{//track whether you are contacting the object
diff --git a/src/gameplay_modes.rs b/src/gameplay_modes.rs
index 5d6d032..f3d98cf 100644
--- a/src/gameplay_modes.rs
+++ b/src/gameplay_modes.rs
@@ -5,9 +5,9 @@ use crate::updatable::Updatable;
 
 #[derive(Clone)]
 pub struct StageElement{
-	stage:StageId,//which stage spawn to send to
-	force:bool,//allow setting to lower spawn id i.e. 7->3
-	behaviour:StageElementBehaviour
+	pub stage:StageId,//which stage spawn to send to
+	pub force:bool,//allow setting to lower spawn id i.e. 7->3
+	pub behaviour:StageElementBehaviour
 }
 impl StageElement{
 	pub fn new(stage_id:u32,force:bool,behaviour:StageElementBehaviour)->Self{
@@ -33,8 +33,16 @@ pub enum StageElementBehaviour{
 
 #[derive(Clone,Copy,Hash,Eq,PartialEq)]
 pub struct CheckpointId(usize);
-#[derive(Clone,Hash,Eq,PartialEq)]
+#[derive(Clone,Hash,Eq,PartialEq,Ord,PartialOrd)]
 pub struct StageId(u32);
+impl StageId{
+	pub const fn id(id:u32)->Self{
+		Self(id)
+	}
+	pub const fn get(self)->u32{
+		self.0
+	}
+}
 pub struct Stage{
 	spawn:ModelId,
 	//open world support lol
@@ -43,6 +51,16 @@ pub struct Stage{
 	ordered_checkpoints:HashMap<CheckpointId,ModelId>,
 	unordered_checkpoints:HashSet<ModelId>,
 }
+impl Stage{
+	pub fn new(spawn:ModelId)->Self{
+		Self{
+			spawn,
+			ordered_checkpoints_count:0,
+			ordered_checkpoints:HashMap::new(),
+			unordered_checkpoints:HashSet::new(),
+		}
+	}
+}
 #[derive(Default)]
 pub struct StageUpdate{
 	//other behaviour models of this stage can have
@@ -62,13 +80,16 @@ pub enum Zone{
 	Finish,
 	Anticheat,
 }
-#[derive(Clone,Hash,Eq,PartialEq)]
+#[derive(Clone,Hash,Eq,PartialEq,Ord,PartialOrd)]
 pub struct ModeId(u32);
 impl ModeId{
 	pub const MAIN:Self=Self(0);
 	pub const BONUS:Self=Self(1);
-	pub const fn mode(mode_id:u32)->Self{
-		Self(mode_id)
+	pub const fn id(id:u32)->Self{
+		Self(id)
+	}
+	pub const fn get(&self)->u32{
+		self.0
 	}
 }
 pub struct Mode{
@@ -81,9 +102,37 @@ pub struct Mode{
 	jump_limit:HashMap<ModelId,u32>,
 }
 impl Mode{
+	pub fn new(style:gameplay_style::StyleModifiers,start:ModelId)->Self{
+		Self{
+			style,
+			start,
+			zones:HashMap::new(),
+			stages:Vec::new(),
+			elements:HashMap::new(),
+			jump_limit:HashMap::new(),
+		}
+	}
+	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_spawn_model_id(&self,stage:StageId)->Option<ModelId>{
 		self.stages.get(stage.0 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_element(&self,model_id:ModelId)->Option<&StageElement>{
+		self.elements.get(&model_id)
+	}
+	pub fn get_jump_limit(&self,model_id:ModelId)->Option<u32>{
+		self.jump_limit.get(&model_id).copied()
+	}
 	//TODO: put this in the SNF
 	pub fn denormalize_data(&mut self){
 		//expand and index normalized data
@@ -165,6 +214,9 @@ impl Modes{
 			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)
 	}
diff --git a/src/gameplay_style.rs b/src/gameplay_style.rs
index ea2bb0c..67ff217 100644
--- a/src/gameplay_style.rs
+++ b/src/gameplay_style.rs
@@ -1,26 +1,26 @@
 use crate::integer::{Time,Ratio64,Planar64,Planar64Vec3};
 
 pub struct StyleModifiers{
-	controls_used:u32,//controls which are allowed to pass into gameplay
-	controls_mask:u32,//controls which are masked from control state (e.g. jump in scroll style)
-	strafe:Option<StrafeSettings>,
-	jump_impulse:JumpImpulse,
-	jump_calculation:JumpCalculation,
-	static_friction:Planar64,
-	kinetic_friction:Planar64,
-	walk_speed:Planar64,
-	walk_accel:Planar64,
-	ladder_speed:Planar64,
-	ladder_accel:Planar64,
-	ladder_dot:Planar64,
-	swim_speed:Planar64,
-	mass:Planar64,
-	mv:Planar64,
-	surf_slope:Option<Planar64>,
-	rocket_force:Option<Planar64>,
-	gravity:Planar64Vec3,
-	hitbox:Hitbox,
-	camera_offset:Planar64Vec3,
+	pub controls_used:u32,//controls which are allowed to pass into gameplay
+	pub controls_mask:u32,//controls which are masked from control state (e.g. jump in scroll style)
+	pub strafe:Option<StrafeSettings>,
+	pub jump_impulse:JumpImpulse,
+	pub jump_calculation:JumpCalculation,
+	pub static_friction:Planar64,
+	pub kinetic_friction:Planar64,
+	pub walk_speed:Planar64,
+	pub walk_accel:Planar64,
+	pub ladder_speed:Planar64,
+	pub ladder_accel:Planar64,
+	pub ladder_dot:Planar64,
+	pub swim_speed:Planar64,
+	pub mass:Planar64,
+	pub mv:Planar64,
+	pub surf_slope:Option<Planar64>,
+	pub rocket_force:Option<Planar64>,
+	pub gravity:Planar64Vec3,
+	pub hitbox:Hitbox,
+	pub camera_offset:Planar64Vec3,
 }
 impl std::default::Default for StyleModifiers{
 	fn default()->Self{
@@ -28,18 +28,18 @@ impl std::default::Default for StyleModifiers{
 	}
 }
 impl StyleModifiers{
-	const CONTROL_MOVEFORWARD:u32=0b00000001;
-	const CONTROL_MOVEBACK:u32=0b00000010;
-	const CONTROL_MOVERIGHT:u32=0b00000100;
-	const CONTROL_MOVELEFT:u32=0b00001000;
-	const CONTROL_MOVEUP:u32=0b00010000;
-	const CONTROL_MOVEDOWN:u32=0b00100000;
-	const CONTROL_JUMP:u32=0b01000000;
-	const CONTROL_ZOOM:u32=0b10000000;
+	pub const CONTROL_MOVEFORWARD:u32=0b00000001;
+	pub const CONTROL_MOVEBACK:u32=0b00000010;
+	pub const CONTROL_MOVERIGHT:u32=0b00000100;
+	pub const CONTROL_MOVELEFT:u32=0b00001000;
+	pub const CONTROL_MOVEUP:u32=0b00010000;
+	pub const CONTROL_MOVEDOWN:u32=0b00100000;
+	pub const CONTROL_JUMP:u32=0b01000000;
+	pub const CONTROL_ZOOM:u32=0b10000000;
 
-	const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X;
-	const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
-	const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
+	pub const RIGHT_DIR:Planar64Vec3=Planar64Vec3::X;
+	pub const UP_DIR:Planar64Vec3=Planar64Vec3::Y;
+	pub const FORWARD_DIR:Planar64Vec3=Planar64Vec3::NEG_Z;
 
 	fn neo()->Self{
 		Self{
@@ -70,7 +70,7 @@ impl StyleModifiers{
 		}
 	}
 
-	fn roblox_bhop()->Self{
+	pub fn roblox_bhop()->Self{
 		Self{
 			controls_used:!0,
 			controls_mask:!0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN),
@@ -209,13 +209,13 @@ impl StyleModifiers{
 	}
 }
 
-enum JumpCalculation{
+pub enum JumpCalculation{
 	Capped,//roblox
 	Energy,//new
 	Linear,//source
 }
 
-enum JumpImpulse{
+pub enum JumpImpulse{
 	FromTime(Time),//jump time is invariant across mass and gravity changes
 	FromHeight(Planar64),//jump height is invariant across mass and gravity changes
 	FromDeltaV(Planar64),//jump velocity is invariant across mass and gravity changes
@@ -226,20 +226,32 @@ enum JumpImpulse{
 //Energy means it adds energy
 //Linear means it linearly adds on
 
-enum EnableStrafe{
+pub enum EnableStrafe{
 	Always,
 	MaskAny(u32),//hsw, shsw
 	MaskAll(u32),
 	//Function(Box<dyn Fn(u32)->bool>),
 }
 
-struct StrafeSettings{
+pub struct StrafeSettings{
 	enable:EnableStrafe,
 	air_accel_limit:Option<Planar64>,
 	tick_rate:Ratio64,
 }
+impl StrafeSettings{
+	pub fn next_tick(&self,time:Time)->Time{
+		Time::from_nanos(self.tick_rate.rhs_div_int(self.tick_rate.mul_int(time.nanos())+1))
+	}
+	pub fn mask(&self,controls:u32)->bool{
+		match self.enable{
+			EnableStrafe::Always=>true,
+			EnableStrafe::MaskAny(mask)=>mask&controls!=0,
+			EnableStrafe::MaskAll(mask)=>mask&controls==mask,
+		}
+	}
+}
 
-enum HitboxMesh{
+pub enum HitboxMesh{
 	Box,//source
 	Cylinder,//roblox
 	//Sphere,//roblox old physics
@@ -248,9 +260,9 @@ enum HitboxMesh{
 	//DualCone,
 }
 
-struct Hitbox{
-	halfsize:Planar64Vec3,
-	mesh:HitboxMesh,
+pub struct Hitbox{
+	pub halfsize:Planar64Vec3,
+	pub mesh:HitboxMesh,
 }
 impl Hitbox{
 	fn roblox()->Self{
diff --git a/src/map.rs b/src/map.rs
index d85c28b..aa4f6bc 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -1,11 +1,11 @@
-use std::collections::HashMap;
-
 use crate::model;
 use crate::gameplay_modes;
+use crate::gameplay_attributes;
 //this is the current map data loaded in memory
 pub struct Map{
-	modes:gameplay_modes::Modes,
-	models:model::Models,
+	pub modes:gameplay_modes::Modes,
+	pub models:model::Models,
+	pub attributes:Vec<gameplay_attributes::CollisionAttributes>,
 	//RenderPattern
-	textures:HashMap<u32,Vec<u8>>,
+	pub textures:Vec<Vec<u8>>,
 }
diff --git a/src/model.rs b/src/model.rs
index f2f9033..36aa4f2 100644
--- a/src/model.rs
+++ b/src/model.rs
@@ -34,6 +34,11 @@ pub struct IndexedPhysicsGroup{
 //This is a superset of PhysicsModel and GraphicsModel
 #[derive(Clone,Copy,Hash,Eq,PartialEq)]
 pub struct IndexedModelId(u32);
+impl IndexedModelId{
+	pub const fn id(id:u32)->Self{
+		Self(id)
+	}
+}
 pub struct IndexedModel{
 	pub unique_pos:Vec<Planar64Vec3>,//Unit32Vec3
 	pub unique_normal:Vec<Planar64Vec3>,//Unit32Vec3
@@ -50,6 +55,11 @@ pub struct IndexedModel{
 
 #[derive(Clone,Copy,Hash,Eq,PartialEq)]
 pub struct ModelId(u32);
+impl ModelId{
+	pub const fn id(id:u32)->Self{
+		Self(id)
+	}
+}
 pub struct Model{
 	pub model:IndexedModelId,
 	pub attributes:gameplay_attributes::CollisionAttributesId,
@@ -61,6 +71,17 @@ pub struct Models{
 	indexed_models:HashMap<IndexedModelId,IndexedModel>,
 	models:HashMap<ModelId,Model>,
 }
+impl Models{
+	pub fn new(
+		indexed_models:HashMap<IndexedModelId,IndexedModel>,
+		models:HashMap<ModelId,Model>,
+	)->Self{
+		Self{
+			indexed_models,
+			models,
+		}
+	}
+}
 impl Updatable<Models> for Models{
 	fn update(&mut self,update:Models){
 		self.indexed_models.extend(update.indexed_models);